diff --git a/bindings/ud.go b/bindings/ud.go
index 8c857233ceca9b660e24b43e93c52cbc1ff5bd83..611d94f49c833f76a6f44f8a43f4f809610470ca 100644
--- a/bindings/ud.go
+++ b/bindings/ud.go
@@ -11,9 +11,12 @@ import (
 	"encoding/json"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/single"
 	"gitlab.com/elixxir/client/ud"
 	"gitlab.com/elixxir/client/xxdk"
+	"gitlab.com/elixxir/crypto/contact"
 	"gitlab.com/elixxir/primitives/fact"
+	"gitlab.com/xx_network/primitives/id"
 	"sync"
 )
 
@@ -99,7 +102,7 @@ type UdNetworkStatus interface {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// Main functions                                                             //
+// Manager functions                                                          //
 ////////////////////////////////////////////////////////////////////////////////
 
 // LoadOrNewUserDiscovery creates a bindings-level user discovery manager.
@@ -267,3 +270,154 @@ func (ud *UserDiscovery) SetAlternativeUserDiscovery(
 func (ud *UserDiscovery) UnsetAlternativeUserDiscovery() error {
 	return ud.api.UnsetAlternativeUserDiscovery()
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// User Discovery Lookup                                                      //
+////////////////////////////////////////////////////////////////////////////////
+
+// UdLookupCallback contains the callback called by LookupUD that returns the
+// contact that matches the passed in ID.
+//
+// Parameters:
+//  - contactBytes - the marshalled bytes of contact.Contact returned from the
+//    lookup, or nil if an error occurs
+//  - err - any errors that occurred in the lookup
+type UdLookupCallback interface {
+	Callback(contactBytes []byte, err error)
+}
+
+// LookupUD returns the public key of the passed ID as known by the user
+// discovery system or returns by the timeout.
+//
+// Parameters:
+//  - e2eID - e2e object ID in the tracker
+//  - udContact - the marshalled bytes of the contact.Contact object
+//  - udIdBytes - the marshalled bytes of the id.ID object for the user
+//    discovery server
+//  - singleRequestParams - the JSON marshalled bytes of single.RequestParams
+//
+// Returns:
+//  - []byte - the JSON marshalled bytes of SingleUseSendReport
+func LookupUD(e2eID int, udContact []byte, cb UdLookupCallback,
+	udIdBytes []byte, singleRequestParamsJSON []byte) ([]byte, error) {
+
+	// Get user from singleton
+	user, err := e2eTrackerSingleton.get(e2eID)
+	if err != nil {
+		return nil, err
+	}
+
+	c, err := contact.Unmarshal(udContact)
+	if err != nil {
+		return nil, err
+	}
+
+	uid, err := id.Unmarshal(udIdBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	var p single.RequestParams
+	err = json.Unmarshal(singleRequestParamsJSON, &p)
+	if err != nil {
+		return nil, err
+	}
+
+	callback := func(c contact.Contact, err error) {
+		cb.Callback(c.Marshal(), err)
+	}
+
+	rids, eid, err := ud.Lookup(user.api, c, callback, uid, p)
+	if err != nil {
+		return nil, err
+	}
+
+	sr := SingleUseSendReport{
+		EphID:       eid.EphId.Int64(),
+		ReceptionID: eid.Source.Marshal(),
+		RoundsList:  makeRoundsList(rids),
+	}
+
+	return json.Marshal(sr)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// User Discovery Search                                                      //
+////////////////////////////////////////////////////////////////////////////////
+
+// UdSearchCallback contains the callback called by SearchUD that returns a list
+// of contact.Contact objects  that match the list of facts passed into
+// SearchUD.
+//
+// Parameters:
+//  - contactListJSON - the JSON marshalled bytes of []contact.Contact, or nil
+//    if an error occurs
+//  - err - any errors that occurred in the search
+type UdSearchCallback interface {
+	Callback(contactListJSON []byte, err error)
+}
+
+// SearchUD searches user discovery for the passed Facts. The searchCallback
+// will return a list of contacts, each having the facts it hit against. This is
+// NOT intended to be used to search for multiple users at once; that can have a
+// privacy reduction. Instead, it is intended to be used to search for a user
+// where multiple pieces of information is known.
+//
+// Parameters:
+//  - e2eID - e2e object ID in the tracker
+//  - udContact - the marshalled bytes of the contact.Contact for the user
+//    discovery server
+//  - factListJSON - the JSON marshalled bytes of fact.FactList
+//  - singleRequestParams - the JSON marshalled bytes of single.RequestParams
+//
+// Returns:
+//  - []byte - the JSON marshalled bytes of SingleUseSendReport
+func SearchUD(e2eID int, udContact []byte, cb UdSearchCallback,
+	factListJSON []byte, singleRequestParamsJSON []byte) ([]byte, error) {
+
+	// Get user from singleton
+	user, err := e2eTrackerSingleton.get(e2eID)
+	if err != nil {
+		return nil, err
+	}
+
+	c, err := contact.Unmarshal(udContact)
+	if err != nil {
+		return nil, err
+	}
+
+	var list fact.FactList
+	err = json.Unmarshal(factListJSON, &list)
+	if err != nil {
+		return nil, err
+	}
+
+	var p single.RequestParams
+	err = json.Unmarshal(singleRequestParamsJSON, &p)
+	if err != nil {
+		return nil, err
+	}
+
+	callback := func(contactList []contact.Contact, err error) {
+		contactListJSON, err2 := json.Marshal(contactList)
+		if err2 != nil {
+			jww.FATAL.Panicf(
+				"Failed to marshal list of contact.Contact: %+v", err2)
+		}
+
+		cb.Callback(contactListJSON, err)
+	}
+
+	rids, eid, err := ud.Search(user.api, c, callback, list, p)
+	if err != nil {
+		return nil, err
+	}
+
+	sr := SingleUseSendReport{
+		EphID:       eid.EphId.Int64(),
+		ReceptionID: eid.Source.Marshal(),
+		RoundsList:  makeRoundsList(rids),
+	}
+
+	return json.Marshal(sr)
+}