Repository: prebid/prebid-server
Pull request #2107 (2021-12-09 08:36:30)

Add GPID Reserved Bidder Name
Implements https://github.com/prebid/prebid-server/issues/2088

I needed to refactor existing code for handling bidderparams due to a parsing problem it introduced. That code failed if any field in `imp.ext` is not an embedded object type, which is a problem since `imp.ext.gpid` is a string. Since this is code is upstream from validation (which makes sense as it could cause an invalid request to become valid) it should not "step on the toes" of the validator. If there is a problem with the request, we should let the validate method continue to handle validation issues.


Patch:
diff --git a/adapters/algorix/algorix.go b/adapters/algorix/algorix.go
index 31d01349..628207d6 100644
--- a/adapters/algorix/algorix.go
+++ b/adapters/algorix/algorix.go
@@ -90,10 +90,22 @@ func getImpAlgoriXExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpAlgorix, error) {
 	return &extImpAlgoriX, nil
 }
 
+func getRegionInfo(region string) string {
+	switch region {
+	case "APAC":
+		return "apac.xyz"
+	case "USE":
+		return "use.xyz"
+	default:
+		return "xyz"
+	}
+}
+
 func (a *adapter) getEndPoint(ext *openrtb_ext.ExtImpAlgorix) (string, error) {
 	endPointParams := macros.EndpointTemplateParams{
 		SourceId:  url.PathEscape(ext.Sid),
 		AccountID: url.PathEscape(ext.Token),
+		Host:      url.PathEscape(getRegionInfo(ext.Region)),
 	}
 	return macros.ResolveMacros(a.EndpointTemplate, endPointParams)
 }
diff --git a/adapters/bizzclick/bizzclick.go b/adapters/bizzclick/bizzclick.go
new file mode 100644
index 00000000..70c2bb0c
--- /dev/null
+++ b/adapters/bizzclick/bizzclick.go
@@ -0,0 +1,192 @@
+package bizzclick
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"text/template"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/prebid/prebid-server/adapters"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/errortypes"
+	"github.com/prebid/prebid-server/macros"
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type adapter struct {
+	endpoint *template.Template
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
+	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
+	}
+
+	bidder := &adapter{
+		endpoint: template,
+	}
+	return bidder, nil
+}
+
+func getHeaders(request *openrtb2.BidRequest) http.Header {
+	headers := http.Header{}
+	headers.Add("Content-Type", "application/json;charset=utf-8")
+	headers.Add("Accept", "application/json")
+	headers.Add("X-Openrtb-Version", "2.5")
+
+	if request.Device != nil {
+		if len(request.Device.UA) > 0 {
+			headers.Add("User-Agent", request.Device.UA)
+		}
+
+		if len(request.Device.IPv6) > 0 {
+			headers.Add("X-Forwarded-For", request.Device.IPv6)
+		}
+
+		if len(request.Device.IP) > 0 {
+			headers.Add("X-Forwarded-For", request.Device.IP)
+		}
+	}
+
+	return headers
+}
+
+func (a *adapter) MakeRequests(
+	openRTBRequest *openrtb2.BidRequest,
+	reqInfo *adapters.ExtraRequestInfo,
+) (
+	requestsToBidder []*adapters.RequestData,
+	errs []error,
+) {
+	bizzclickExt, err := a.getImpressionExt(&openRTBRequest.Imp[0])
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	for idx := range openRTBRequest.Imp {
+		openRTBRequest.Imp[idx].Ext = nil
+	}
+	url, err := a.buildEndpointURL(bizzclickExt)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	reqJSON, err := json.Marshal(openRTBRequest)
+	if err != nil {
+		return nil, []error{err}
+	}
+
+	return []*adapters.RequestData{{
+		Method:  http.MethodPost,
+		Body:    reqJSON,
+		Uri:     url,
+		Headers: getHeaders(openRTBRequest),
+	}}, nil
+}
+
+func (a *adapter) getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtBizzclick, error) {
+	var bidderExt adapters.ExtImpBidder
+	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "ext.bidder not provided",
+		}
+	}
+	var bizzclickExt openrtb_ext.ExtBizzclick
+	if err := json.Unmarshal(bidderExt.Bidder, &bizzclickExt); err != nil {
+		return nil, &errortypes.BadInput{
+			Message: "ext.bidder not provided",
+		}
+	}
+
+	return &bizzclickExt, nil
+}
+
+func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtBizzclick) (string, error) {
+	endpointParams := macros.EndpointTemplateParams{AccountID: params.AccountID, SourceId: params.PlacementID}
+	return macros.ResolveMacros(a.endpoint, endpointParams)
+}
+
+func (a *adapter) checkResponseStatusCodes(response *adapters.ResponseData) error {
+	if response.StatusCode == http.StatusBadRequest {
+		return &errortypes.BadInput{
+			Message: fmt.Sprintf("Unexpected status code: [ %d ]", response.StatusCode),
+		}
+	}
+
+	if response.StatusCode == http.StatusServiceUnavailable {
+		return &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode),
+		}
+	}
+
+	if response.StatusCode != http.StatusOK {
+		return &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: [ %d ]. Run with request.debug = 1 for more info", response.StatusCode),
+		}
+	}
+
+	return nil
+}
+
+func (a *adapter) MakeBids(
+	openRTBRequest *openrtb2.BidRequest,
+	requestToBidder *adapters.RequestData,
+	bidderRawResponse *adapters.ResponseData,
+) (
+	bidderResponse *adapters.BidderResponse,
+	errs []error,
+) {
+	if bidderRawResponse.StatusCode == http.StatusNoContent {
+		return nil, nil
+	}
+
+	httpStatusError := a.checkResponseStatusCodes(bidderRawResponse)
+	if httpStatusError != nil {
+		return nil, []error{httpStatusError}
+	}
+
+	responseBody := bidderRawResponse.Body
+	var bidResp openrtb2.BidResponse
+	if err := json.Unmarshal(responseBody, &bidResp); err != nil {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Bad Server Response",
+		}}
+	}
+
+	if len(bidResp.SeatBid) == 0 {
+		return nil, []error{&errortypes.BadServerResponse{
+			Message: "Empty SeatBid array",
+		}}
+	}
+
+	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
+	sb := bidResp.SeatBid[0]
+	var bidsArray []*adapters.TypedBid
+
+	for idx, bid := range sb.Bid {
+		bidsArray = append(bidsArray, &adapters.TypedBid{
+			Bid:     &sb.Bid[idx],
+			BidType: getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp),
+		})
+	}
+
+	bidResponse.Bids = bidsArray
+	return bidResponse, nil
+}
+
+func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
+	mediaType := openrtb_ext.BidTypeBanner
+	for _, imp := range imps {
+		if imp.ID == impId {
+			if imp.Video != nil {
+				mediaType = openrtb_ext.BidTypeVideo
+			} else if imp.Native != nil {
+				mediaType = openrtb_ext.BidTypeNative
+			}
+			return mediaType
+		}
+	}
+	return mediaType
+}
diff --git a/adapters/marsmedia/marsmedia.go b/adapters/marsmedia/marsmedia.go
index 0f4fad5a..65ffff25 100644
--- a/adapters/marsmedia/marsmedia.go
+++ b/adapters/marsmedia/marsmedia.go
@@ -83,7 +83,7 @@ func (a *MarsmediaAdapter) MakeRequests(requestIn *openrtb2.BidRequest, reqInfo
 		}}
 	}
 
-	uri := a.URI + "&zone=" + marsmediaExt.ZoneID
+	uri := a.URI + "&zone=" + marsmediaExt.ZoneID.String()
 	headers := http.Header{}
 	headers.Add("Content-Type", "application/json;charset=utf-8")
 	headers.Add("Accept", "application/json")
diff --git a/adapters/openrtb_util.go b/adapters/openrtb_util.go
index 5587cac7..2816e730 100644
--- a/adapters/openrtb_util.go
+++ b/adapters/openrtb_util.go
@@ -4,11 +4,12 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+
 	"github.com/mxmCherry/openrtb/v15/openrtb2"
 	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
-func ExtractAdapterReqBidderParams(bidRequest *openrtb2.BidRequest) (map[string]json.RawMessage, error) {
+func ExtractReqExtBidderParamsMap(bidRequest *openrtb2.BidRequest) (map[string]json.RawMessage, error) {
 	if bidRequest == nil {
 		return nil, errors.New("error bidRequest should not be nil")
 	}
@@ -34,15 +35,14 @@ func ExtractAdapterReqBidderParams(bidRequest *openrtb2.BidRequest) (map[string]
 	return bidderParams, nil
 }
 
-func ExtractReqExtBidderParams(bidReq *openrtb2.BidRequest) (map[string]map[string]json.RawMessage, error) {
-	if bidReq == nil {
+func ExtractReqExtBidderParamsEmbeddedMap(bidRequest *openrtb2.BidRequest) (map[string]map[string]json.RawMessage, error) {
+	if bidRequest == nil {
 		return nil, errors.New("error bidRequest should not be nil")
 	}
 
 	reqExt := &openrtb_ext.ExtRequest{}
-	if len(bidReq.Ext) > 0 {
-		err := json.Unmarshal(bidReq.Ext, &reqExt)
-		if err != nil {
+	if len(bidRequest.Ext) > 0 {
+		if err := json.Unmarshal(bidRequest.Ext, &reqExt); err != nil {
 			return nil, fmt.Errorf("error decoding Request.ext : %s", err.Error())
 		}
 	}
@@ -52,8 +52,7 @@ func ExtractReqExtBidderParams(bidReq *openrtb2.BidRequest) (map[string]map[stri
 	}
 
 	var bidderParams map[string]map[string]json.RawMessage
-	err := json.Unmarshal(reqExt.Prebid.BidderParams, &bidderParams)
-	if err != nil {
+	if err := json.Unmarshal(reqExt.Prebid.BidderParams, &bidderParams); err != nil {
 		return nil, err
 	}
 
diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go
index 799d3364..f855f917 100644
--- a/adapters/pubmatic/pubmatic.go
+++ b/adapters/pubmatic/pubmatic.go
@@ -305,7 +305,7 @@ func parseImpressionObject(imp *openrtb2.Imp, extractWrapperExtFromImp, extractP
 // extractPubmaticWrapperExtFromRequest parse the imp to get it ready to send to pubmatic
 func extractPubmaticWrapperExtFromRequest(request *openrtb2.BidRequest) (*pubmaticWrapperExt, error) {
 	var wrpExt pubmaticWrapperExt
-	reqExtBidderParams, err := adapters.ExtractAdapterReqBidderParams(request)
+	reqExtBidderParams, err := adapters.ExtractReqExtBidderParamsMap(request)
 	if err != nil {
 		return nil, err
 	}
diff --git a/config/config.go b/config/config.go
index 41809aea..f9dbc274 100644
--- a/config/config.go
+++ b/config/config.go
@@ -788,7 +788,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.adxcg.disabled", true)
 	v.SetDefault("adapters.adyoulike.endpoint", "https://broker.omnitagjs.com/broker/bid?partnerId=19340f4f097d16f41f34fc0274981ca4")
 	v.SetDefault("adapters.aja.endpoint", "https://ad.as.amanad.adtdp.com/v1/bid/4")
-	v.SetDefault("adapters.algorix.endpoint", "https://xyz.svr-algorix.com/rtb/sa?sid={{.SourceId}}&token={{.AccountID}}")
+	v.SetDefault("adapters.algorix.endpoint", "https://{{.Host}}.svr-algorix.com/rtb/sa?sid={{.SourceId}}&token={{.AccountID}}")
 	v.SetDefault("adapters.amx.endpoint", "http://pbs.amxrtb.com/auction/openrtb")
 	v.SetDefault("adapters.applogy.endpoint", "http://rtb.applogy.com/v1/prebid")
 	v.SetDefault("adapters.appnexus.endpoint", "http://ib.adnxs.com/openrtb2") // Docs: https://wiki.appnexus.com/display/supply/Incoming+Bid+Request+from+SSPs
@@ -804,6 +804,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("adapters.bidmachine.endpoint", "https://{{.Host}}.bidmachine.io")
 	v.SetDefault("adapters.bidmyadz.endpoint", "http://endpoint.bidmyadz.com/c0f68227d14ed938c6c49f3967cbe9bc")
 	v.SetDefault("adapters.bidscube.endpoint", "http://supply.bidscube.com/?c=o&m=rtb")
+	v.SetDefault("adapters.bizzclick.endpoint", "http://us-e-node1.bizzclick.com/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}")
 	v.SetDefault("adapters.bmtm.endpoint", "https://one.elitebidder.com/api/pbs")
 	v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs")
 	v.SetDefault("adapters.coinzilla.endpoint", "http://request-global.czilladx.com/serve/prebid-server.php")
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 8e9725f3..07d869dc 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -332,115 +332,138 @@ func parseTimeout(requestJson []byte, defaultTimeout time.Duration) time.Duratio
 	return defaultTimeout
 }
 
-// mergeBidderParams merges bidder parameters passed at req.ext level with imp[].ext level.
-// Preference is given to parameters at imp[].ext level over req.ext level.
-// Parameters at req.ext level are propagated to adapters as is without any validation.
+// mergeBidderParams merges bidder parameters in req.ext down to the imp[].ext level, with
+// priority given to imp[].ext in case of a conflict. No validation of bidder parameters or
+// of the ext json is performed. Unmarshal errors are not expected since the ext json was
+// validated during the bid request unmarshal. If there is an unmarshal error for some reason,
+// we rely on downstream validation to return the error.
 func mergeBidderParams(req *openrtb_ext.RequestWrapper) error {
-	reqBidderParams, err := adapters.ExtractReqExtBidderParams(req.BidRequest)
-	if err != nil {
-		return err
+	reqBidderParams, err := adapters.ExtractReqExtBidderParamsEmbeddedMap(req.BidRequest)
+	if err != nil || len(reqBidderParams) == 0 {
+		return nil
 	}
 
-	impCpy := make([]openrtb2.Imp, 0, len(req.BidRequest.Imp))
-	for _, imp := range req.BidRequest.Imp {
+	imps := make([]openrtb2.Imp, 0, len(req.BidRequest.Imp))
+	for impIndex, imp := range req.BidRequest.Imp {
 		updatedImp := imp
 
 		if len(imp.Ext) == 0 {
-			impCpy = append(impCpy, updatedImp)
+			imps = append(imps, updatedImp)
 			continue
 		}
 
-		var impExt map[string]map[string]json.RawMessage
-		err := json.Unmarshal(imp.Ext, &impExt)
-		if err != nil {
-			return err
+		var impExt map[string]json.RawMessage
+		if err := json.Unmarshal(imp.Ext, &impExt); err != nil {
+			// imp.ext should be parsable, but we haven't validated yet so there may be another issue. ignore it here
+			// and let the validation catch it later on.
+			imps = append(imps, updatedImp)
+			continue
 		}
 
 		//merges bidder parameters passed at req.ext level with imp[].ext level.
-		err = addMissingReqExtParamsInImpExt(impExt, reqBidderParams)
+		impExtModified, err := addMissingReqExtParamsInImpExt(impExt, reqBidderParams)
 		if err != nil {
-			return err
+			return fmt.Errorf("error processing bidder parameters for imp[%d]: %s", impIndex, err.Error())
 		}
 
 		//merges bidder parameters passed at req.ext level with imp[].ext.prebid.bidder level.
-		err = addMissingReqExtParamsInImpExtPrebid(impExt, reqBidderParams)
+		impExtPrebidModified, err := addMissingReqExtParamsInImpExtPrebid(impExt, reqBidderParams)
 		if err != nil {
-			return err
+			return fmt.Errorf("error processing bidder parameters for imp[%d]: %s", impIndex, err.Error())
 		}
 
-		iExt, err := json.Marshal(impExt)
-		if err != nil {
-			return fmt.Errorf("error marshalling imp[].ext : %s", err.Error())
+		if impExtModified || impExtPrebidModified {
+			updatedImpExt, err := json.Marshal(impExt)
+			if err != nil {
+				return fmt.Errorf("error processing bidder parameters for imp[%d]: error marshalling ext:%s", impIndex, err.Error())
+			}
+			updatedImp.Ext = updatedImpExt
 		}
-		updatedImp.Ext = iExt
-		impCpy = append(impCpy, updatedImp)
+
+		imps = append(imps, updatedImp)
 	}
 
-	req.BidRequest.Imp = impCpy
+	req.BidRequest.Imp = imps
 	return nil
 }
 
 // addMissingReqExtParamsInImpExtPrebid merges bidder parameters passed at req.ext level with imp[].ext.prebid.bidder level.
-func addMissingReqExtParamsInImpExtPrebid(impExtBidder map[string]map[string]json.RawMessage, reqExtParams map[string]map[string]json.RawMessage) error {
-	var bidderParams map[string]json.RawMessage
-	if impExtBidder["prebid"] != nil && impExtBidder["prebid"]["bidder"] != nil {
-		err := json.Unmarshal(impExtBidder["prebid"]["bidder"], &bidderParams)
-		if err != nil {
-			return err
-		}
+func addMissingReqExtParamsInImpExtPrebid(impExtByKey map[string]json.RawMessage, reqExtParams map[string]map[string]json.RawMessage) (bool, error) {
+	if _, prebidSectionExists := impExtByKey["prebid"]; !prebidSectionExists {
+		return false, nil
 	}
 
-	if len(bidderParams) != 0 {
-		for bidder, bidderExt := range bidderParams {
-			if !isBidderToValidate(bidder) {
-				continue
-			}
+	var prebidByKey map[string]json.RawMessage
+	if err := json.Unmarshal(impExtByKey["prebid"], &prebidByKey); err != nil {
+		// prebid should be parsable, but we haven't validated yet so there may be another issue. ignore it here
+		// and let the validation catch it later on.
+		return false, nil
+	}
+
+	if _, prebidBidderSectionExists := prebidByKey["bidder"]; !prebidBidderSectionExists {
+		return false, nil
+	}
 
-			var params map[string]json.RawMessage
-			err := json.Unmarshal(bidderExt, &params)
+	var prebidBidderByBidder map[string]json.RawMessage
+	if err := json.Unmarshal(prebidByKey["bidder"], &prebidBidderByBidder); err != nil {
+		// added for completeness, but not possible. would be caught by the earlier unmarshal call.
+		return false, nil
+	}
 
-			for key, value := range reqExtParams[bidder] {
-				if _, present := params[key]; !present {
-					params[key] = value
-				}
-			}
+	anyModified, err := addMissingReqExtParamsInImpExt(prebidBidderByBidder, reqExtParams)
 
-			paramsJson, err := json.Marshal(params)
-			if err != nil {
-				return err
-			}
-			bidderParams[bidder] = paramsJson
+	if anyModified && err == nil {
+		// marshal imp.ext.prebid.bidder
+		prebidBidderJSON, err := json.Marshal(prebidBidderByBidder)
+		if err != nil {
+			return anyModified, err
 		}
+		prebidByKey["bidder"] = prebidBidderJSON
 
-		bidderParamsJson, err := json.Marshal(bidderParams)
+		// marshal imp.ext.prebid
+		prebidJSON, err := json.Marshal(prebidByKey)
 		if err != nil {
-			return err
+			return anyModified, err
 		}
-		impExtBidder["prebid"]["bidder"] = bidderParamsJson
+		impExtByKey["prebid"] = prebidJSON
 	}
 
-	return nil
+	return anyModified, err
 }
 
 // addMissingReqExtParamsInImpExt merges bidder parameters passed at req.ext level with imp[].ext level.
-func addMissingReqExtParamsInImpExt(impExtBidder map[string]map[string]json.RawMessage, reqExtParams map[string]map[string]json.RawMessage) error {
-	for bidder, bidderExt := range impExtBidder {
+func addMissingReqExtParamsInImpExt(impExtByBidder map[string]json.RawMessage, reqExtParams map[string]map[string]json.RawMessage) (bool, error) {
+	anyModified := false
+	for bidder, bidderExt := range impExtByBidder {
 		if !isBidderToValidate(bidder) {
 			continue
 		}
 
-		wasModified := false
+		var params map[string]json.RawMessage
+		if err := json.Unmarshal(bidderExt, &params); err != nil {
+			// bidder should be parsable, but we haven't validated yet so there may be another issue. ignore it here
+			// and let the validation later on catch it.
+			continue
+		}
+
+		modified := false
 		for key, value := range reqExtParams[bidder] {
-			if _, present := bidderExt[key]; !present {
-				bidderExt[key] = value
-				wasModified = true
+			if _, present := params[key]; !present {
+				params[key] = value
+				modified = true
 			}
 		}
-		if wasModified {
-			impExtBidder[bidder] = bidderExt
+
+		if modified {
+			paramsJson, err := json.Marshal(params)
+			if err != nil {
+				return anyModified, fmt.Errorf("error marshalling ext.prebid.bidder: %s", err.Error())
+			}
+			impExtByBidder[bidder] = paramsJson
+			anyModified = true
 		}
 	}
-	return nil
+	return anyModified, nil
 }
 
 func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp bool) []error {
@@ -1173,12 +1196,12 @@ func isBidderToValidate(bidder string) bool {
 		return false
 	case openrtb_ext.BidderReservedData:
 		return false
+	case openrtb_ext.BidderReservedGPID:
+		return false
 	case openrtb_ext.BidderReservedPrebid:
 		return false
 	case openrtb_ext.BidderReservedSKAdN:
 		return false
-	case openrtb_ext.BidderReservedBidder:
-		return false
 	default:
 		return true
 	}
diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json b/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json
index 5baaa09d..76dc1c2b 100644
--- a/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json
+++ b/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json
@@ -27,5 +27,5 @@
     }
   },
   "expectedReturnCode": 400,
-  "expectedErrorMessage": "Invalid request: error decoding Request.ext : request.ext.prebid.cache requires one of the \"bids\" or \"vastxml\" properties\n"
+  "expectedErrorMessage": "Invalid request: request.ext is invalid: request.ext.prebid.cache requires one of the \"bids\" or \"vastxml\" properties\n"
 }
diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/imp-ext-invalid-params.json b/endpoints/openrtb2/sample-requests/invalid-whole/imp-ext-invalid-params.json
index b1e9b784..d9ed59c8 100644
--- a/endpoints/openrtb2/sample-requests/invalid-whole/imp-ext-invalid-params.json
+++ b/endpoints/openrtb2/sample-requests/invalid-whole/imp-ext-invalid-params.json
@@ -20,5 +20,5 @@
     }
   },
   "expectedReturnCode": 400,
-  "expectedErrorMessage": "Invalid request: json: cannot unmarshal string into Go value of type map[string]json.RawMessage"
+  "expectedErrorMessage": "Invalid request: request.imp[0].ext.appnexus failed validation.\n(root): Invalid type. Expected: object, given: string\n"
 }
diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge-reserved-keyword.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge-reserved-keyword.json
deleted file mode 100644
index 982f86cb..00000000
--- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-ext-bidder-params-merge-reserved-keyword.json
+++ /dev/null
@@ -1,116 +0,0 @@
-{
-  "description": "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext.prebid.bidder for reserved keyword",
-  "mockBidRequest": {
-    "id": "some-request-id",
-    "site": {
-      "page": "test.somepage.com"
-    },
-    "imp": [
-      {
-        "id": "my-imp-id",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 600
-            }
-          ]
-        },
-        "pmp": {
-          "deals": [
-            {
-              "id": "some-deal-id"
-            }
-          ]
-        },
-        "ext": {
-          "prebid": {
-            "bidder": {
-              "bidder": {
-                "param1": 111111
-              },
-              "appnexus": {
-                "placementId": 111111
-              },
-              "pubmatic": {
-                "publisherId": "1234",
-                "wrapper": {
-                  "profile": 1234
-                }
-              }
-            }
-          }
-        }
-      }
-    ],
-    "ext": {
-      "prebid": {
-        "targeting": {
-          "pricegranularity": "low"
-        },
-        "cache": {
-          "bids": {}
-        },
-        "bidderparams": {
-          "bidder": {
-            "param2": 111111
-          },
-          "appnexus": {
-            "placementId": 222222
-          },
-          "pubmatic": {
-            "publisherId": "5678",
-            "wrapper": {
-              "profile": 5678,
-              "version": 2
-            }
-          }
-        }
-      }
-    }
-  },
-  "expectedReqExt": {
-    "prebid": {
-      "targeting": {
-        "pricegranularity": "low"
-      },
-      "cache": {
-        "bids": {}
-      },
-      "bidderparams": {
-        "bidder": {
-          "param2": 111111
-        },
-        "appnexus": {
-          "placementId": 222222
-        },
-        "pubmatic": {
-          "publisherId": "5678",
-          "wrapper": {
-            "profile": 5678,
-            "version": 2
-          }
-        }
-      }
-    }
-  },
-  "expectedImpExt": {
-    "prebid": {
-      "bidder": {
-        "bidder": {
-          "param1": 111111
-        },
-        "appnexus": {
-          "placementId": 111111
-        },
-        "pubmatic": {
-          "publisherId": "1234",
-          "wrapper": {
-            "profile": 1234
-          }
-        }
-      }
-    }
-  },
-  "expectedReturnCode": 200
-}
\ No newline at end of file
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 5d99b7f3..0f14b75c 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -39,6 +39,7 @@ import (
 	"github.com/prebid/prebid-server/adapters/bidmachine"
 	"github.com/prebid/prebid-server/adapters/bidmyadz"
 	"github.com/prebid/prebid-server/adapters/bidscube"
+	"github.com/prebid/prebid-server/adapters/bizzclick"
 	"github.com/prebid/prebid-server/adapters/bmtm"
 	"github.com/prebid/prebid-server/adapters/brightroll"
 	"github.com/prebid/prebid-server/adapters/coinzilla"
@@ -177,6 +178,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderBidmachine:        bidmachine.Builder,
 		openrtb_ext.BidderBidmyadz:          bidmyadz.Builder,
 		openrtb_ext.BidderBidsCube:          bidscube.Builder,
+		openrtb_ext.BidderBizzclick:         bizzclick.Builder,
 		openrtb_ext.BidderBmtm:              bmtm.Builder,
 		openrtb_ext.BidderBrightroll:        brightroll.Builder,
 		openrtb_ext.BidderCoinzilla:         coinzilla.Builder,
diff --git a/exchange/utils.go b/exchange/utils.go
index 23ffe5e5..48f76b2a 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -223,7 +223,7 @@ func getAuctionBidderRequests(req AuctionRequest,
 		return nil, []error{err}
 	}
 
-	bidderParamsInReqExt, err := adapters.ExtractReqExtBidderParams(req.BidRequest)
+	bidderParamsInReqExt, err := adapters.ExtractReqExtBidderParamsEmbeddedMap(req.BidRequest)
 	if err != nil {
 		return nil, []error{err}
 	}
@@ -464,6 +464,10 @@ func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map
 		sanitizedImpExt[openrtb_ext.SKAdNExtKey] = v
 	}
 
+	if v, exists := impExt[string(openrtb_ext.GPIDKey)]; exists {
+		sanitizedImpExt[openrtb_ext.GPIDKey] = v
+	}
+
 	return sanitizedImpExt, nil
 }
 
@@ -493,6 +497,7 @@ func isSpecialField(bidder string) bool {
 	return bidder == openrtb_ext.FirstPartyDataContextExtKey ||
 		bidder == openrtb_ext.FirstPartyDataExtKey ||
 		bidder == openrtb_ext.SKAdNExtKey ||
+		bidder == openrtb_ext.GPIDKey ||
 		bidder == openrtb_ext.PrebidExtKey
 }
 
diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go
index 53a5afab..368424c5 100644
--- a/metrics/prometheus/prometheus.go
+++ b/metrics/prometheus/prometheus.go
@@ -1,6 +1,7 @@
 package prometheusmetrics
 
 import (
+	"fmt"
 	"strconv"
 	"time"
 
@@ -8,11 +9,13 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prometheus/client_golang/prometheus"
+	promCollector "github.com/prometheus/client_golang/prometheus/collectors"
 )
 
 // Metrics defines the Prometheus metrics backing the MetricsEngine implementation.
 type Metrics struct {
-	Registry *prometheus.Registry
+	Registerer prometheus.Registerer
+	Gatherer   *prometheus.Registry
 
 	// General Metrics
 	connectionsClosed            prometheus.Counter
@@ -133,246 +136,253 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
 	queuedRequestTimeBuckets := []float64{0, 1, 5, 30, 60, 120, 180, 240, 300}
 
 	metrics := Metrics{}
-	metrics.Registry = prometheus.NewRegistry()
+	reg := prometheus.NewRegistry()
 	metrics.metricsDisabled = disabledMetrics
 
-	metrics.connectionsClosed = newCounterWithoutLabels(cfg, metrics.Registry,
+	metrics.connectionsClosed = newCounterWithoutLabels(cfg, reg,
 		"connections_closed",
 		"Count of successful connections closed to Prebid Server.")
 
-	metrics.connectionsError = newCounter(cfg, metrics.Registry,
+	metrics.connectionsError = newCounter(cfg, reg,
 		"connections_error",
 		"Count of errors for connection open and close attempts to Prebid Server labeled by type.",
 		[]string{connectionErrorLabel})
 
-	metrics.connectionsOpened = newCounterWithoutLabels(cfg, metrics.Registry,
+	metrics.connectionsOpened = newCounterWithoutLabels(cfg, reg,
 		"connections_opened",
 		"Count of successful connections opened to Prebid Server.")
 
-	metrics.cookieSync = newCounter(cfg, metrics.Registry,
+	metrics.cookieSync = newCounter(cfg, reg,
 		"cookie_sync_requests",
 		"Count of cookie sync requests to Prebid Server.",
 		[]string{statusLabel})
 
-	metrics.setUid = newCounter(cfg, metrics.Registry,
+	metrics.setUid = newCounter(cfg, reg,
 		"setuid_requests",
 		"Count of set uid requests to Prebid Server.",
 		[]string{statusLabel})
 
-	metrics.impressions = newCounter(cfg, metrics.Registry,
+	metrics.impressions = newCounter(cfg, reg,
 		"impressions_requests",
 		"Count of requested impressions to Prebid Server labeled by type.",
 		[]string{isBannerLabel, isVideoLabel, isAudioLabel, isNativeLabel})
 
-	metrics.impressionsLegacy = newCounterWithoutLabels(cfg, metrics.Registry,
+	metrics.impressionsLegacy = newCounterWithoutLabels(cfg, reg,
 		"impressions_requests_legacy",
 		"Count of requested impressions to Prebid Server using the legacy endpoint.")
 
-	metrics.prebidCacheWriteTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.prebidCacheWriteTimer = newHistogramVec(cfg, reg,
 		"prebidcache_write_time_seconds",
 		"Seconds to write to Prebid Cache labeled by success or failure. Failure timing is limited by Prebid Server enforced timeouts.",
 		[]string{successLabel},
 		cacheWriteTimeBuckets)
 
-	metrics.requests = newCounter(cfg, metrics.Registry,
+	metrics.requests = newCounter(cfg, reg,
 		"requests",
 		"Count of total requests to Prebid Server labeled by type and status.",
 		[]string{requestTypeLabel, requestStatusLabel})
 
-	metrics.requestsTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.requestsTimer = newHistogramVec(cfg, reg,
 		"request_time_seconds",
 		"Seconds to resolve successful Prebid Server requests labeled by type.",
 		[]string{requestTypeLabel},
 		standardTimeBuckets)
 
-	metrics.requestsWithoutCookie = newCounter(cfg, metrics.Registry,
+	metrics.requestsWithoutCookie = newCounter(cfg, reg,
 		"requests_without_cookie",
 		"Count of total requests to Prebid Server without a cookie labeled by type.",
 		[]string{requestTypeLabel})
 
-	metrics.storedImpressionsCacheResult = newCounter(cfg, metrics.Registry,
+	metrics.storedImpressionsCacheResult = newCounter(cfg, reg,
 		"stored_impressions_cache_performance",
 		"Count of stored impression cache requests attempts by hits or miss.",
 		[]string{cacheResultLabel})
 
-	metrics.storedRequestCacheResult = newCounter(cfg, metrics.Registry,
+	metrics.storedRequestCacheResult = newCounter(cfg, reg,
 		"stored_request_cache_performance",
 		"Count of stored request cache requests attempts by hits or miss.",
 		[]string{cacheResultLabel})
 
-	metrics.accountCacheResult = newCounter(cfg, metrics.Registry,
+	metrics.accountCacheResult = newCounter(cfg, reg,
 		"account_cache_performance",
 		"Count of account cache lookups by hits or miss.",
 		[]string{cacheResultLabel})
 
-	metrics.storedAccountFetchTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.storedAccountFetchTimer = newHistogramVec(cfg, reg,
 		"stored_account_fetch_time_seconds",
 		"Seconds to fetch stored accounts labeled by fetch type",
 		[]string{storedDataFetchTypeLabel},
 		standardTimeBuckets)
 
-	metrics.storedAccountErrors = newCounter(cfg, metrics.Registry,
+	metrics.storedAccountErrors = newCounter(cfg, reg,
 		"stored_account_errors",
 		"Count of stored account errors by error type",
 		[]string{storedDataErrorLabel})
 
-	metrics.storedAMPFetchTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.storedAMPFetchTimer = newHistogramVec(cfg, reg,
 		"stored_amp_fetch_time_seconds",
 		"Seconds to fetch stored AMP requests labeled by fetch type",
 		[]string{storedDataFetchTypeLabel},
 		standardTimeBuckets)
 
-	metrics.storedAMPErrors = newCounter(cfg, metrics.Registry,
+	metrics.storedAMPErrors = newCounter(cfg, reg,
 		"stored_amp_errors",
 		"Count of stored AMP errors by error type",
 		[]string{storedDataErrorLabel})
 
-	metrics.storedCategoryFetchTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.storedCategoryFetchTimer = newHistogramVec(cfg, reg,
 		"stored_category_fetch_time_seconds",
 		"Seconds to fetch stored categories labeled by fetch type",
 		[]string{storedDataFetchTypeLabel},
 		standardTimeBuckets)
 
-	metrics.storedCategoryErrors = newCounter(cfg, metrics.Registry,
+	metrics.storedCategoryErrors = newCounter(cfg, reg,
 		"stored_category_errors",
 		"Count of stored category errors by error type",
 		[]string{storedDataErrorLabel})
 
-	metrics.storedRequestFetchTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.storedRequestFetchTimer = newHistogramVec(cfg, reg,
 		"stored_request_fetch_time_seconds",
 		"Seconds to fetch stored requests labeled by fetch type",
 		[]string{storedDataFetchTypeLabel},
 		standardTimeBuckets)
 
-	metrics.storedRequestErrors = newCounter(cfg, metrics.Registry,
+	metrics.storedRequestErrors = newCounter(cfg, reg,
 		"stored_request_errors",
 		"Count of stored request errors by error type",
 		[]string{storedDataErrorLabel})
 
-	metrics.storedVideoFetchTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.storedVideoFetchTimer = newHistogramVec(cfg, reg,
 		"stored_video_fetch_time_seconds",
 		"Seconds to fetch stored video labeled by fetch type",
 		[]string{storedDataFetchTypeLabel},
 		standardTimeBuckets)
 
-	metrics.storedVideoErrors = newCounter(cfg, metrics.Registry,
+	metrics.storedVideoErrors = newCounter(cfg, reg,
 		"stored_video_errors",
 		"Count of stored video errors by error type",
 		[]string{storedDataErrorLabel})
 
-	metrics.timeoutNotifications = newCounter(cfg, metrics.Registry,
+	metrics.timeoutNotifications = newCounter(cfg, reg,
 		"timeout_notification",
 		"Count of timeout notifications triggered, and if they were successfully sent.",
 		[]string{successLabel})
 
-	metrics.dnsLookupTimer = newHistogram(cfg, metrics.Registry,
+	metrics.dnsLookupTimer = newHistogram(cfg, reg,
 		"dns_lookup_time",
 		"Seconds to resolve DNS",
 		standardTimeBuckets)
 
-	metrics.tlsHandhakeTimer = newHistogram(cfg, metrics.Registry,
+	metrics.tlsHandhakeTimer = newHistogram(cfg, reg,
 		"tls_handshake_time",
 		"Seconds to perform TLS Handshake",
 		standardTimeBuckets)
 
-	metrics.privacyCCPA = newCounter(cfg, metrics.Registry,
+	metrics.privacyCCPA = newCounter(cfg, reg,
 		"privacy_ccpa",
 		"Count of total requests to Prebid Server where CCPA was provided by source and opt-out .",
 		[]string{sourceLabel, optOutLabel})
 
-	metrics.privacyCOPPA = newCounter(cfg, metrics.Registry,
+	metrics.privacyCOPPA = newCounter(cfg, reg,
 		"privacy_coppa",
 		"Count of total requests to Prebid Server where the COPPA flag was set by source",
 		[]string{sourceLabel})
 
-	metrics.privacyTCF = newCounter(cfg, metrics.Registry,
+	metrics.privacyTCF = newCounter(cfg, reg,
 		"privacy_tcf",
 		"Count of TCF versions for requests where GDPR was enforced by source and version.",
 		[]string{versionLabel, sourceLabel})
 
-	metrics.privacyLMT = newCounter(cfg, metrics.Registry,
+	metrics.privacyLMT = newCounter(cfg, reg,
 		"privacy_lmt",
 		"Count of total requests to Prebid Server where the LMT flag was set by source",
 		[]string{sourceLabel})
 
 	if !metrics.metricsDisabled.AdapterGDPRRequestBlocked {
-		metrics.adapterGDPRBlockedRequests = newCounter(cfg, metrics.Registry,
+		metrics.adapterGDPRBlockedRequests = newCounter(cfg, reg,
 			"adapter_gdpr_requests_blocked",
 			"Count of total bidder requests blocked due to unsatisfied GDPR purpose 2 legal basis",
 			[]string{adapterLabel})
 	}
 
-	metrics.adapterBids = newCounter(cfg, metrics.Registry,
+	metrics.adapterBids = newCounter(cfg, reg,
 		"adapter_bids",
 		"Count of bids labeled by adapter and markup delivery type (adm or nurl).",
 		[]string{adapterLabel, markupDeliveryLabel})
 
-	metrics.adapterErrors = newCounter(cfg, metrics.Registry,
+	metrics.adapterErrors = newCounter(cfg, reg,
 		"adapter_errors",
 		"Count of errors labeled by adapter and error type.",
 		[]string{adapterLabel, adapterErrorLabel})
 
-	metrics.adapterPanics = newCounter(cfg, metrics.Registry,
+	metrics.adapterPanics = newCounter(cfg, reg,
 		"adapter_panics",
 		"Count of panics labeled by adapter.",
 		[]string{adapterLabel})
 
-	metrics.adapterPrices = newHistogramVec(cfg, metrics.Registry,
+	metrics.adapterPrices = newHistogramVec(cfg, reg,
 		"adapter_prices",
 		"Monetary value of the bids labeled by adapter.",
 		[]string{adapterLabel},
 		priceBuckets)
 
-	metrics.adapterRequests = newCounter(cfg, metrics.Registry,
+	metrics.adapterRequests = newCounter(cfg, reg,
 		"adapter_requests",
 		"Count of requests labeled by adapter, if has a cookie, and if it resulted in bids.",
 		[]string{adapterLabel, cookieLabel, hasBidsLabel})
 
 	if !metrics.metricsDisabled.AdapterConnectionMetrics {
-		metrics.adapterCreatedConnections = newCounter(cfg, metrics.Registry,
+		metrics.adapterCreatedConnections = newCounter(cfg, reg,
 			"adapter_connection_created",
 			"Count that keeps track of new connections when contacting adapter bidder endpoints.",
 			[]string{adapterLabel})
 
-		metrics.adapterReusedConnections = newCounter(cfg, metrics.Registry,
+		metrics.adapterReusedConnections = newCounter(cfg, reg,
 			"adapter_connection_reused",
 			"Count that keeps track of reused connections when contacting adapter bidder endpoints.",
 			[]string{adapterLabel})
 
-		metrics.adapterConnectionWaitTime = newHistogramVec(cfg, metrics.Registry,
+		metrics.adapterConnectionWaitTime = newHistogramVec(cfg, reg,
 			"adapter_connection_wait",
 			"Seconds from when the connection was requested until it is either created or reused",
 			[]string{adapterLabel},
 			standardTimeBuckets)
 	}
 
-	metrics.adapterRequestsTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.adapterRequestsTimer = newHistogramVec(cfg, reg,
 		"adapter_request_time_seconds",
 		"Seconds to resolve each successful request labeled by adapter.",
 		[]string{adapterLabel},
 		standardTimeBuckets)
 
-	metrics.syncerRequests = newCounter(cfg, metrics.Registry,
+	metrics.syncerRequests = newCounter(cfg, reg,
 		"syncer_requests",
 		"Count of cookie sync requests where a syncer is a candidate to be synced labeled by syncer key and status.",
 		[]string{syncerLabel, statusLabel})
 
-	metrics.syncerSets = newCounter(cfg, metrics.Registry,
+	metrics.syncerSets = newCounter(cfg, reg,
 		"syncer_sets",
 		"Count of setuid set requests for a syncer labeled by syncer key and status.",
 		[]string{syncerLabel, statusLabel})
 
-	metrics.accountRequests = newCounter(cfg, metrics.Registry,
+	metrics.accountRequests = newCounter(cfg, reg,
 		"account_requests",
 		"Count of total requests to Prebid Server labeled by account.",
 		[]string{accountLabel})
 
-	metrics.requestsQueueTimer = newHistogramVec(cfg, metrics.Registry,
+	metrics.requestsQueueTimer = newHistogramVec(cfg, reg,
 		"request_queue_time",
 		"Seconds request was waiting in queue",
 		[]string{requestTypeLabel, requestStatusLabel},
 		queuedRequestTimeBuckets)
 
+	metrics.Gatherer = reg
+
+	metricsPrefix := fmt.Sprintf("%s_%s_", cfg.Namespace, cfg.Subsystem)
+
+	metrics.Registerer = prometheus.WrapRegistererWithPrefix(metricsPrefix, reg)
+	metrics.Registerer.MustRegister(promCollector.NewGoCollector())
+
 	preloadLabelValues(&metrics, syncerKeys)
 
 	return &metrics
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index e8b6ce4c..7ebfe631 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -32,9 +32,9 @@ const (
 	BidderReservedContext BidderName = "context" // Reserved for first party data.
 	BidderReservedData    BidderName = "data"    // Reserved for first party data.
 	BidderReservedGeneral BidderName = "general" // Reserved for non-bidder specific messages when using a map keyed on the bidder name.
+	BidderReservedGPID    BidderName = "gpid"    // Reserved for Global Placement ID (GPID).
 	BidderReservedPrebid  BidderName = "prebid"  // Reserved for Prebid Server configuration.
 	BidderReservedSKAdN   BidderName = "skadn"   // Reserved for Apple's SKAdNetwork OpenRTB extension.
-	BidderReservedBidder  BidderName = "bidder"  // Reserved for passing bidder parameters.
 )
 
 // IsBidderNameReserved returns true if the specified name is a case insensitive match for a reserved bidder name.
@@ -55,15 +55,15 @@ func IsBidderNameReserved(name string) bool {
 		return true
 	}
 
-	if strings.EqualFold(name, string(BidderReservedSKAdN)) {
+	if strings.EqualFold(name, string(BidderReservedGPID)) {
 		return true
 	}
 
-	if strings.EqualFold(name, string(BidderReservedPrebid)) {
+	if strings.EqualFold(name, string(BidderReservedSKAdN)) {
 		return true
 	}
 
-	if strings.EqualFold(name, string(BidderReservedBidder)) {
+	if strings.EqualFold(name, string(BidderReservedPrebid)) {
 		return true
 	}
 
@@ -114,6 +114,7 @@ const (
 	BidderBidmachine        BidderName = "bidmachine"
 	BidderBidmyadz          BidderName = "bidmyadz"
 	BidderBidsCube          BidderName = "bidscube"
+	BidderBizzclick         BidderName = "bizzclick"
 	BidderBmtm              BidderName = "bmtm"
 	BidderBrightroll        BidderName = "brightroll"
 	BidderCoinzilla         BidderName = "coinzilla"
@@ -256,6 +257,7 @@ func CoreBidderNames() []BidderName {
 		BidderBidmachine,
 		BidderBidmyadz,
 		BidderBidsCube,
+		BidderBizzclick,
 		BidderBmtm,
 		BidderBrightroll,
 		BidderCoinzilla,
diff --git a/openrtb_ext/imp_algorix.go b/openrtb_ext/imp_algorix.go
index 3661018d..d395600c 100644
--- a/openrtb_ext/imp_algorix.go
+++ b/openrtb_ext/imp_algorix.go
@@ -6,4 +6,5 @@ type ExtImpAlgorix struct {
 	Token       string `json:"token"`
 	PlacementId string `json:"placementId"`
 	AppId       string `json:"appId"`
+	Region      string `json:"region"`
 }
diff --git a/openrtb_ext/imp_bizzclick.go b/openrtb_ext/imp_bizzclick.go
new file mode 100644
index 00000000..5090a822
--- /dev/null
+++ b/openrtb_ext/imp_bizzclick.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ExtBizzclick struct {
+	AccountID   string `json:"accountId"`
+	PlacementID string `json:"placementId"`
+}
diff --git a/openrtb_ext/imp_marsmedia.go b/openrtb_ext/imp_marsmedia.go
index c27fe2db..e07b6d7c 100644
--- a/openrtb_ext/imp_marsmedia.go
+++ b/openrtb_ext/imp_marsmedia.go
@@ -1,6 +1,8 @@
 package openrtb_ext
 
+import "encoding/json"
+
 // ExtImpMarsmedia defines the contract for bidrequest.imp[i].ext.marsmedia
 type ExtImpMarsmedia struct {
-	ZoneID string `json:"zoneId"`
+	ZoneID json.Number `json:"zoneId"`
 }
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index a65e8d3c..dba30863 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -14,6 +14,9 @@ const FirstPartyDataContextExtKey = "context"
 // SKAdNExtKey defines the field name within request.ext reserved for Apple's SKAdNetwork.
 const SKAdNExtKey = "skadn"
 
+// GPIDKey defines the field name within request.ext reserved for the Global Placement ID (GPID),
+const GPIDKey = "gpid"
+
 // NativeExchangeSpecificLowerBound defines the lower threshold of exchange specific types for native ads. There is no upper bound.
 const NativeExchangeSpecificLowerBound = 500
 
diff --git a/server/prometheus.go b/server/prometheus.go
index 6f0a0b2d..33114c86 100644
--- a/server/prometheus.go
+++ b/server/prometheus.go
@@ -19,7 +19,7 @@ func newPrometheusServer(cfg *config.Configuration, metrics *metricsconfig.Detai
 	}
 	return &http.Server{
 		Addr: cfg.Host + ":" + strconv.Itoa(cfg.Metrics.Prometheus.Port),
-		Handler: promhttp.HandlerFor(proMetrics.Registry, promhttp.HandlerOpts{
+		Handler: promhttp.HandlerFor(proMetrics.Gatherer, promhttp.HandlerOpts{
 			ErrorLog:            loggerForPrometheus{},
 			MaxRequestsInFlight: 5,
 			Timeout:             cfg.Metrics.Prometheus.Timeout(),
diff --git a/static/bidder-info/bizzclick.yaml b/static/bidder-info/bizzclick.yaml
new file mode 100644
index 00000000..48921c39
--- /dev/null
+++ b/static/bidder-info/bizzclick.yaml
@@ -0,0 +1,13 @@
+maintainer:
+  email: "support@bizzclick.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - native
diff --git a/static/bidder-params/algorix.json b/static/bidder-params/algorix.json
index 675d9793..2fcbd6ec 100644
--- a/static/bidder-params/algorix.json
+++ b/static/bidder-params/algorix.json
@@ -21,6 +21,11 @@
     "appId": {
       "type": "string",
       "description": "An ID which identifies this app of the impression"
+    },
+    "region": {
+      "type": "string",
+      "enum": ["APAC", "USE"],
+      "description": "Server region for PBS request, null for global, APAC for SG Region, USE for US Region"
     }
   },
   "required": ["sid", "token"]
diff --git a/static/bidder-params/bizzclick.json b/static/bidder-params/bizzclick.json
new file mode 100644
index 00000000..429e2948
--- /dev/null
+++ b/static/bidder-params/bizzclick.json
@@ -0,0 +1,19 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Bizzclick Adapter Params",
+  "description": "A schema which validates params accepted by the Bizzclick adapter",
+  "type": "object",
+  "properties": {
+    "accountId": {
+      "type": "string",
+      "description": "Account id",
+      "minLength": 1
+    },
+    "placementId": {
+      "type": "string",
+      "description": "PlacementId id",
+      "minLength": 1
+    }
+  },
+  "required": ["accountId", "placementId"]
+}
\ No newline at end of file
diff --git a/static/bidder-params/marsmedia.json b/static/bidder-params/marsmedia.json
index b812eb89..7555261a 100644
--- a/static/bidder-params/marsmedia.json
+++ b/static/bidder-params/marsmedia.json
@@ -5,8 +5,8 @@
   "type": "object",
   "properties": {
     "zoneId": {
-      "type": "string",
-      "description": "Zone ID to use."
+      "type": ["integer", "string"],
+      "description": "The zone ID from Mars Media Group."
     }
   }
 }


Test patch:
diff --git a/adapters/algorix/algorix_test.go b/adapters/algorix/algorix_test.go
index 55c8b9ef..79cf8609 100644
--- a/adapters/algorix/algorix_test.go
+++ b/adapters/algorix/algorix_test.go
@@ -11,7 +11,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderAlgorix, config.Adapter{
-		Endpoint: "https://test.com?sid={{.SourceId}}&token={{.AccountID}}"})
+		Endpoint: "https://{{.Host}}.test.com?sid={{.SourceId}}&token={{.AccountID}}"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
diff --git a/adapters/algorix/algorixtest/exemplary/sample-banner-with-palcementid.json b/adapters/algorix/algorixtest/exemplary/sample-banner-with-palcementid.json
index 4d1853f0..e50fb3a4 100644
--- a/adapters/algorix/algorixtest/exemplary/sample-banner-with-palcementid.json
+++ b/adapters/algorix/algorixtest/exemplary/sample-banner-with-palcementid.json
@@ -13,7 +13,8 @@
             "sid": "testSid",
             "token": "testToken",
             "placementId": "testPlacementId",
-            "appId": "testAppId"
+            "appId": "testAppId",
+            "region": "USE"
           }
         }
       }
@@ -23,7 +24,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://use.xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
@@ -40,7 +41,8 @@
                   "sid": "testSid",
                   "token": "testToken",
                   "placementId": "testPlacementId",
-                  "appId": "testAppId"
+                  "appId": "testAppId",
+                  "region": "USE"
                 }
               }
             }
diff --git a/adapters/algorix/algorixtest/exemplary/sample-banner.json b/adapters/algorix/algorixtest/exemplary/sample-banner.json
index 118f89c1..d558214a 100644
--- a/adapters/algorix/algorixtest/exemplary/sample-banner.json
+++ b/adapters/algorix/algorixtest/exemplary/sample-banner.json
@@ -10,7 +10,8 @@
         "ext": {
           "bidder": {
             "sid": "testSid",
-            "token": "testToken"
+            "token": "testToken",
+            "region": "APAC"
           }
         }
       }
@@ -20,7 +21,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://apac.xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
@@ -34,7 +35,8 @@
               "ext": {
                 "bidder": {
                   "sid": "testSid",
-                  "token": "testToken"
+                  "token": "testToken",
+                  "region": "APAC"
                 }
               }
             }
diff --git a/adapters/algorix/algorixtest/exemplary/sample-native.json b/adapters/algorix/algorixtest/exemplary/sample-native.json
index 3b24bd3a..36a0d1af 100644
--- a/adapters/algorix/algorixtest/exemplary/sample-native.json
+++ b/adapters/algorix/algorixtest/exemplary/sample-native.json
@@ -22,7 +22,7 @@
   "httpcalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/algorix/algorixtest/exemplary/sample-nobid.json b/adapters/algorix/algorixtest/exemplary/sample-nobid.json
index c378ff0d..d4fbd561 100644
--- a/adapters/algorix/algorixtest/exemplary/sample-nobid.json
+++ b/adapters/algorix/algorixtest/exemplary/sample-nobid.json
@@ -22,7 +22,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/algorix/algorixtest/exemplary/sample-video.json b/adapters/algorix/algorixtest/exemplary/sample-video.json
index df3f6dcd..edb8fddb 100644
--- a/adapters/algorix/algorixtest/exemplary/sample-video.json
+++ b/adapters/algorix/algorixtest/exemplary/sample-video.json
@@ -26,7 +26,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/algorix/algorixtest/supplemental/bad_response.json b/adapters/algorix/algorixtest/supplemental/bad_response.json
index 17d84673..83ed5663 100644
--- a/adapters/algorix/algorixtest/supplemental/bad_response.json
+++ b/adapters/algorix/algorixtest/supplemental/bad_response.json
@@ -20,7 +20,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/algorix/algorixtest/supplemental/status_400.json b/adapters/algorix/algorixtest/supplemental/status_400.json
index f384c3b9..2085bf3c 100644
--- a/adapters/algorix/algorixtest/supplemental/status_400.json
+++ b/adapters/algorix/algorixtest/supplemental/status_400.json
@@ -20,7 +20,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/algorix/algorixtest/supplemental/status_500.json b/adapters/algorix/algorixtest/supplemental/status_500.json
index ca6cf14b..c7820917 100644
--- a/adapters/algorix/algorixtest/supplemental/status_500.json
+++ b/adapters/algorix/algorixtest/supplemental/status_500.json
@@ -20,7 +20,7 @@
   "httpCalls": [
     {
       "expectedRequest": {
-        "uri": "https://test.com?sid=testSid&token=testToken",
+        "uri": "https://xyz.test.com?sid=testSid&token=testToken",
         "body": {
           "id": "test-request-id",
           "imp": [
diff --git a/adapters/algorix/params_test.go b/adapters/algorix/params_test.go
index 3830b372..c33a3417 100644
--- a/adapters/algorix/params_test.go
+++ b/adapters/algorix/params_test.go
@@ -36,6 +36,8 @@ func TestInvalidParams(t *testing.T) {
 var validParams = []string{
 	`{"sid": "11233", "token": "sin", "placementId": "123", "appId": "abc"}`,
 	`{"sid": "11244", "token": "iad"}`,
+	`{"sid": "11244", "token": "iad", "region": "APAC"}`,
+	`{"sid": "11244", "token": "iad", "region": "USE"}`,
 }
 
 var invalidParams = []string{
@@ -47,4 +49,5 @@ var invalidParams = []string{
 	`{"sid": "11233", "token": "sin", "placementId": 123, "appId": "abc"}`,
 	`{"sid": "11233", "token": "sin", "placementId": "123", "appId": 456}`,
 	`{"sid": "11233", "token": "sin", "placementId": 123, "appId": 456}`,
+	`{"sid": "11233", "token": "sin", "region": 123}`,
 }
diff --git a/adapters/bizzclick/bizzclick_test.go b/adapters/bizzclick/bizzclick_test.go
new file mode 100644
index 00000000..baebb540
--- /dev/null
+++ b/adapters/bizzclick/bizzclick_test.go
@@ -0,0 +1,28 @@
+package bizzclick
+
+import (
+	"testing"
+
+	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestJsonSamples(t *testing.T) {
+	bidder, buildErr := Builder(openrtb_ext.BidderBizzclick, config.Adapter{
+		Endpoint: "http://us.example.com/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}"})
+
+	if buildErr != nil {
+		t.Fatalf("Builder returned unexpected error %v", buildErr)
+	}
+
+	adapterstest.RunJSONBidderTest(t, "bizzclicktest", bidder)
+}
+
+func TestEndpointTemplateMalformed(t *testing.T) {
+	_, buildErr := Builder(openrtb_ext.BidderBizzclick, config.Adapter{
+		Endpoint: "{{Malformed}}"})
+
+	assert.Error(t, buildErr)
+}
diff --git a/adapters/bizzclick/bizzclicktest/exemplary/banner-app.json b/adapters/bizzclick/bizzclicktest/exemplary/banner-app.json
new file mode 100644
index 00000000..1ba1d155
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/exemplary/banner-app.json
@@ -0,0 +1,150 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "banner": {
+          "w": 320,
+          "h": 50
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ]
+        },
+        "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "banner": {
+                "w": 320,
+                "h": 50
+              },
+              "tagid": "ogTAGID"
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "Awesome App",
+            "bundle": "com.app.awesome",
+            "domain": "awesomeapp.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "1",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ],
+              "type": "banner",
+              "seat": "bizzclick"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "1",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20",
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bizzclick/bizzclicktest/exemplary/banner-web.json b/adapters/bizzclick/bizzclicktest/exemplary/banner-web.json
new file mode 100644
index 00000000..5c2baba6
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/exemplary/banner-web.json
@@ -0,0 +1,187 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id1",
+        "tagid": "ogTAGID",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      },     
+       {
+        "id": "some-impression-id2",
+        "tagid": "ogTAGID",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ]
+        },
+        "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id1",
+              "tagid": "ogTAGID",
+              "banner": {
+                "w":320,
+                "h":50
+              }
+            },
+            {
+              "id": "some-impression-id2",
+              "tagid": "ogTAGID",
+              "banner": {
+                "w":320,
+                "h":50
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id1",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                },
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e162",
+                  "impid": "some-impression-id2",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 320,
+                  "h": 50
+                }
+              ],
+              "type": "banner",
+              "seat": "bizzclick"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id1",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ],
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        },
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e162",
+            "impid": "some-impression-id2",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ],
+            "w": 320,
+            "h": 50
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bizzclick/bizzclicktest/exemplary/native-app.json b/adapters/bizzclick/bizzclicktest/exemplary/native-app.json
new file mode 100644
index 00000000..764f6006
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/exemplary/native-app.json
@@ -0,0 +1,147 @@
+{
+    "mockBidRequest": {
+      "id": "some-request-id",
+      "device": {
+        "ua": "test-user-agent",
+        "ip": "123.123.123.123",
+        "language": "en",
+        "dnt": 0
+      },
+      "tmax": 1000,
+      "user": {
+        "buyeruid": "awesome-user"
+      },
+      "app": {
+        "publisher": {
+          "id": "123456789"
+        },
+        "cat": [
+          "IAB22-1"
+        ],
+        "bundle": "com.app.awesome",
+        "name": "Awesome App",
+        "domain": "awesomeapp.com",
+        "id": "123456789"
+      },
+      "imp": [
+        {
+          "id": "some-impression-id",
+          "tagid": "ogTAGID",
+          "native": {
+            "ver":"1.1",
+            "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+          },
+          "ext": {
+            "bidder": {
+              "accountId": "accountId",
+              "placementId": "placementId"
+            }
+          }
+        }
+      ]
+    },
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "headers": {
+            "Content-Type": [
+              "application/json;charset=utf-8"
+            ],
+            "Accept": [
+              "application/json"
+            ],
+            "X-Openrtb-Version": [
+              "2.5"
+            ],
+            "User-Agent": [
+              "test-user-agent"
+            ],
+            "X-Forwarded-For": [
+              "123.123.123.123"
+            ]
+          },
+          "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+          "body": {
+            "id": "some-request-id",
+            "device": {
+              "ua": "test-user-agent",
+              "ip": "123.123.123.123",
+              "language": "en",
+              "dnt": 0
+            },
+            "imp": [
+              {
+                "id": "some-impression-id",
+                "native": {
+                  "ver":"1.1",
+                  "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+                },
+                "tagid": "ogTAGID"
+              }
+            ],
+            "app": {
+              "id": "123456789",
+              "name": "Awesome App",
+              "bundle": "com.app.awesome",
+              "domain": "awesomeapp.com",
+              "cat": [
+                "IAB22-1"
+              ],
+              "publisher": {
+                "id": "123456789"
+              }
+            },
+            "user": {
+              "buyeruid": "awesome-user"
+            },
+            "tmax": 1000
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "awesome-resp-id",
+            "seatbid": [
+              {
+                "bid": [
+                  {
+                    "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                    "impid": "some-impression-id",
+                    "price": 3.5,
+                    "adm": "awesome-markup",
+                    "adomain": [
+                      "awesome.com"
+                    ],
+                    "crid": "20"
+                  }
+                ],
+                "type": "native",
+                "seat": "bizzclick"
+              }
+            ],
+            "cur": "USD"
+          }
+        }
+      }
+    ],
+    "expectedBidResponses": [
+      {
+        "bids":[
+          {
+            "bid":  {
+              "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+              "impid": "some-impression-id",
+              "price": 3.5,
+              "adm": "awesome-markup",
+              "crid": "20",
+              "adomain": [
+                "awesome.com"
+              ]
+            },
+            "type": "native"
+          }
+        ]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/bizzclick/bizzclicktest/exemplary/native-web.json b/adapters/bizzclick/bizzclicktest/exemplary/native-web.json
new file mode 100644
index 00000000..04a33e6f
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/exemplary/native-web.json
@@ -0,0 +1,133 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "native": {
+          "ver":"1.1",
+          "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "2607:fb90:f27:4512:d800:cb23:a603:e245"
+          ]
+        },
+        "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "tagid": "ogTAGID",
+              "native": {
+                "ver":"1.1",
+                "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}"
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20"
+                }
+              ],
+              "seat": "bizzclick"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ]
+          },
+          "type": "native"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bizzclick/bizzclicktest/exemplary/video-app.json b/adapters/bizzclick/bizzclicktest/exemplary/video-app.json
new file mode 100644
index 00000000..18d38b8e
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/exemplary/video-app.json
@@ -0,0 +1,159 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ]
+        },
+        "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              },
+              "tagid": "ogTAGID"
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "Awesome App",
+            "bundle": "com.app.awesome",
+            "domain": "awesomeapp.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720
+                }
+              ],
+              "seat": "bizzclick"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids":[
+        {
+          "bid":  {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "crid": "20",
+            "adomain": [
+              "awesome.com"
+            ],
+            "w": 1280,
+            "h": 720
+          },
+          "type": "video"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/bizzclick/bizzclicktest/exemplary/video-web.json b/adapters/bizzclick/bizzclicktest/exemplary/video-web.json
new file mode 100644
index 00000000..57ec0641
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/exemplary/video-web.json
@@ -0,0 +1,157 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ]
+        },
+        "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "tagid": "ogTAGID",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              }
+            }
+          ],
+          "site": {
+            "page": "test.com",
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+                  "impid": "some-impression-id",
+                  "price": 3.5,
+                  "adm": "awesome-markup",
+                  "adomain": [
+                    "awesome.com"
+                  ],
+                  "crid": "20",
+                  "w": 1280,
+                  "h": 720,
+                  "ext": {
+                    "prebid": {
+                      "type": "video"
+                    }
+                  }
+                }
+              ],
+              "seat": "bizzclick"
+            }
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+  "expectedBidResponses": [
+    {
+      "bids": [
+        {
+          "bid": {
+            "id": "a3ae1b4e2fc24a4fb45540082e98e161",
+            "impid": "some-impression-id",
+            "price": 3.5,
+            "adm": "awesome-markup",
+            "adomain": [
+              "awesome.com"
+            ],
+            "crid": "20",
+            "w": 1280,
+            "h": 720,
+            "ext": {
+              "prebid": {
+                "type": "video"
+              }
+            }
+          },
+          "type":"video"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/adapters/bizzclick/bizzclicktest/supplemental/empty-seatbid-array.json b/adapters/bizzclick/bizzclicktest/supplemental/empty-seatbid-array.json
new file mode 100644
index 00000000..7b244c40
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/supplemental/empty-seatbid-array.json
@@ -0,0 +1,131 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": [
+            "application/json;charset=utf-8"
+          ],
+          "Accept": [
+            "application/json"
+          ],
+          "X-Openrtb-Version": [
+            "2.5"
+          ],
+          "User-Agent": [
+            "test-user-agent"
+          ],
+          "X-Forwarded-For": [
+            "123.123.123.123"
+          ]
+        },
+        "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+        "body": {
+          "id": "some-request-id",
+          "device": {
+            "ua": "test-user-agent",
+            "ip": "123.123.123.123",
+            "language": "en",
+            "dnt": 0
+          },
+          "imp": [
+            {
+              "id": "some-impression-id",
+              "video": {
+                "mimes": [
+                  "video/mp4"
+                ],
+                "minduration": 120,
+                "maxduration": 150,
+                "w": 640,
+                "h": 480
+              },
+              "tagid": "ogTAGID"
+            }
+          ],
+          "app": {
+            "id": "123456789",
+            "name": "Awesome App",
+            "bundle": "com.app.awesome",
+            "domain": "awesomeapp.com",
+            "cat": [
+              "IAB22-1"
+            ],
+            "publisher": {
+              "id": "123456789"
+            }
+          },
+          "user": {
+            "buyeruid": "awesome-user"
+          },
+          "tmax": 1000
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "awesome-resp-id",
+          "seatbid": [
+          ],
+          "cur": "USD"
+        }
+      }
+    }
+  ],
+    "mockResponse": {
+      "status": 200,
+      "body": "invalid response"
+    },
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Empty SeatBid array",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bizzclick/bizzclicktest/supplemental/invalid-bizzclick-ext-object.json b/adapters/bizzclick/bizzclicktest/supplemental/invalid-bizzclick-ext-object.json
new file mode 100644
index 00000000..77752d01
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/supplemental/invalid-bizzclick-ext-object.json
@@ -0,0 +1,29 @@
+{
+  "expectedMakeRequestsErrors": [
+    {
+      "value": "ext.bidder not provided",
+      "comparison": "literal"
+    }
+  ],
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "my-adcode",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": "Awesome"
+      }
+    ],
+    "site": {
+      "page": "test.com"
+    }
+  },
+  "httpCalls": []
+}
diff --git a/adapters/bizzclick/bizzclicktest/supplemental/invalid-response.json b/adapters/bizzclick/bizzclicktest/supplemental/invalid-response.json
new file mode 100644
index 00000000..57f36d3a
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/supplemental/invalid-response.json
@@ -0,0 +1,112 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "device": {
+      "ua": "test-user-agent",
+      "ip": "123.123.123.123",
+      "language": "en",
+      "dnt": 0
+    },
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "banner": {
+          "w":320,
+          "h":50
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "headers": {
+        "Content-Type": [
+          "application/json;charset=utf-8"
+        ],
+        "Accept": [
+          "application/json"
+        ],
+        "X-Openrtb-Version": [
+          "2.5"
+        ],
+        "User-Agent": [
+          "test-user-agent"
+        ],
+        "X-Forwarded-For": [
+          "123.123.123.123"
+        ]
+      },
+      "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+      "body": {
+        "id": "some-request-id",
+        "device": {
+          "ua": "test-user-agent",
+          "ip": "123.123.123.123",
+          "language": "en",
+          "dnt": 0
+        },
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "banner": {
+                "w":320,
+                "h":50
+            },
+            "tagid": "ogTAGID"
+          }
+        ],
+        "app": {
+          "id": "123456789",
+          "name": "Awesome App",
+          "bundle": "com.app.awesome",
+          "domain": "awesomeapp.com",
+          "cat": [
+            "IAB22-1"
+          ],
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "awesome-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 200,
+      "body": "invalid response"
+    }
+  }],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Bad Server Response",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bizzclick/bizzclicktest/supplemental/status-code-bad-request.json b/adapters/bizzclick/bizzclicktest/supplemental/status-code-bad-request.json
new file mode 100644
index 00000000..f0a3b91a
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/supplemental/status-code-bad-request.json
@@ -0,0 +1,93 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "app": {
+      "publisher": {
+        "id": "123456789"
+      },
+      "cat": [
+        "IAB22-1"
+      ],
+      "bundle": "com.app.awesome",
+      "name": "Awesome App",
+      "domain": "awesomeapp.com",
+      "id": "123456789"
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "video": {
+          "mimes": [
+            "video/mp4"
+          ],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            },
+            "tagid": "ogTAGID"
+          }
+        ],
+        "app":  {
+          "publisher": {
+            "id": "123456789"
+          },
+          "cat": [
+            "IAB22-1"
+          ],
+          "bundle": "com.app.awesome",
+          "name": "Awesome App",
+          "domain": "awesomeapp.com",
+          "id": "123456789"
+        },
+        "user": {
+          "buyeruid": "awesome-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 400
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: [ 400 ]",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bizzclick/bizzclicktest/supplemental/status-code-no-content.json b/adapters/bizzclick/bizzclicktest/supplemental/status-code-no-content.json
new file mode 100644
index 00000000..62aecf58
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/supplemental/status-code-no-content.json
@@ -0,0 +1,69 @@
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [{
+      "id": "some-impression-id",
+      "tagid": "ogTAGID",
+      "video": {
+        "mimes": ["video/mp4"],
+        "w": 640,
+        "h": 480,
+        "minduration": 120,
+        "maxduration": 150
+      },
+      "ext": {
+        "bidder": {
+          "accountId": "accountId",
+          "placementId": "placementId"
+        }
+      }
+    }]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+      "body": {
+        "id": "some-request-id",
+        "imp": [{
+          "id": "some-impression-id",
+          "tagid": "ogTAGID",
+          "video": {
+            "mimes": [
+              "video/mp4"
+            ],
+            "minduration": 120,
+            "maxduration": 150,
+            "w": 640,
+            "h": 480
+          }
+        }],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "awesome-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 204
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": []
+}
\ No newline at end of file
diff --git a/adapters/bizzclick/bizzclicktest/supplemental/status-code-other-error.json b/adapters/bizzclick/bizzclicktest/supplemental/status-code-other-error.json
new file mode 100644
index 00000000..398f0b67
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/supplemental/status-code-other-error.json
@@ -0,0 +1,79 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "tagid": "ogTAGID",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "awesome-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 306
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Unexpected status code: [ 306 ]. Run with request.debug = 1 for more info",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bizzclick/bizzclicktest/supplemental/status-code-service-unavailable.json b/adapters/bizzclick/bizzclicktest/supplemental/status-code-service-unavailable.json
new file mode 100644
index 00000000..733b5f3f
--- /dev/null
+++ b/adapters/bizzclick/bizzclicktest/supplemental/status-code-service-unavailable.json
@@ -0,0 +1,79 @@
+
+{
+  "mockBidRequest": {
+    "id": "some-request-id",
+    "tmax": 1000,
+    "user": {
+      "buyeruid": "awesome-user"
+    },
+    "site": {
+      "page": "test.com",
+      "publisher": {
+        "id": "123456789"
+      }
+    },
+    "imp": [
+      {
+        "id": "some-impression-id",
+        "tagid": "ogTAGID",
+        "video": {
+          "mimes": ["video/mp4"],
+          "w": 640,
+          "h": 480,
+          "minduration": 120,
+          "maxduration": 150
+        },
+        "ext": {
+          "bidder": {
+            "accountId": "accountId",
+            "placementId": "placementId"
+          }
+        }
+      }
+    ]
+  },
+
+  "httpCalls": [{
+    "expectedRequest": {
+      "uri": "http://us.example.com/bid?rtb_seat_id=placementId&secret_key=accountId",
+      "body": {
+        "id": "some-request-id",
+        "imp": [
+          {
+            "id": "some-impression-id",
+            "tagid": "ogTAGID",
+            "video": {
+              "mimes": [
+                "video/mp4"
+              ],
+              "minduration": 120,
+              "maxduration": 150,
+              "w": 640,
+              "h": 480
+            }
+          }
+        ],
+        "site": {
+          "page": "test.com",
+          "publisher": {
+            "id": "123456789"
+          }
+        },
+        "user": {
+          "buyeruid": "awesome-user"
+        },
+        "tmax": 1000
+      }
+    },
+    "mockResponse": {
+      "status": 503
+    }
+  }],
+  "expectedBidResponses": [],
+  "expectedMakeBidsErrors": [
+    {
+      "value": "Something went wrong, please contact your Account Manager. Status Code: [ 503 ] ",
+      "comparison": "literal"
+    }
+  ]
+}
diff --git a/adapters/bizzclick/params_test.go b/adapters/bizzclick/params_test.go
new file mode 100644
index 00000000..34211b00
--- /dev/null
+++ b/adapters/bizzclick/params_test.go
@@ -0,0 +1,50 @@
+package bizzclick
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/prebid/prebid-server/openrtb_ext"
+)
+
+var validParams = []string{
+	`{  "accountId": "hash",  "placementId": "hash"}`,
+}
+
+func TestValidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, validParam := range validParams {
+		if err := validator.Validate(openrtb_ext.BidderBizzclick, json.RawMessage(validParam)); err != nil {
+			t.Errorf("Schema rejected Bizzclick params: %s", validParam)
+		}
+	}
+}
+
+var invalidParams = []string{
+	``,
+	`null`,
+	`true`,
+	`5`,
+	`4.2`,
+	`[]`,
+	`{}`,
+	`{"adCode": "string", "seatCode": 5, "originalPublisherid": "string"}`,
+	`{  "accountid": "" }`,
+}
+
+func TestInvalidParams(t *testing.T) {
+	validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
+	if err != nil {
+		t.Fatalf("Failed to fetch the json-schemas. %v", err)
+	}
+
+	for _, invalidParam := range invalidParams {
+		if err := validator.Validate(openrtb_ext.BidderBizzclick, json.RawMessage(invalidParam)); err == nil {
+			t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+		}
+	}
+}
diff --git a/adapters/marsmedia/marsmediatest/exemplary/simple-banner-int-param.json b/adapters/marsmedia/marsmediatest/exemplary/simple-banner-int-param.json
new file mode 100644
index 00000000..4804a873
--- /dev/null
+++ b/adapters/marsmedia/marsmediatest/exemplary/simple-banner-int-param.json
@@ -0,0 +1,106 @@
+{
+    "mockBidRequest": {
+      "id": "test-request-id",
+      "imp": [
+        {
+          "id": "test-imp-id",
+          "at" : 1,
+          "banner": {
+            "format": [
+              {
+                  "w": 300,
+                  "h": 250
+              }
+            ]
+          },
+          "ext": {
+            "bidder": {
+              "zoneId": 9999
+            }
+          }
+        }
+      ]
+    },
+  
+    "httpCalls": [
+      {
+        "expectedRequest": {
+          "uri": "http://bid306.rtbsrv.com/bidder/?bid=f3xtet&zone=9999",
+          "body": {
+            "id": "test-request-id",
+            "at" : 1,
+            "imp": [
+              {
+                "id": "test-imp-id",
+                "banner": {
+                  "format": [
+                    {
+                        "w": 300,
+                        "h": 250
+                    }
+                  ],
+                  "w": 300,
+                  "h": 250
+                },
+                "ext": {
+                  "bidder": {
+                    "zoneId": 9999
+                  }
+                }
+              }
+            ]
+          }
+        },
+        "mockResponse": {
+          "status": 200,
+          "body": {
+            "id": "test-request-id",
+            "seatbid": [
+              {
+                "seat": "marsmedia",
+                "bid": [{
+                  "id": "1",
+                  "impid": "test-imp-id",
+                  "price": 1.1,
+                  "adid": "17586",
+                  "adm": "some-test-ad",
+                  "nurl" : "some-test-nurl",
+                  "iurl" : "some-test-iurl",
+                  "adomain": ["wooga.com"],
+                  "cid": "17586",
+                  "crid": "17586",
+                  "h": 250,
+                  "w": 300
+                }]
+              }
+            ],
+            "bidid": "5778926625248726496",
+            "cur": "USD"
+          }
+        }
+      }
+    ],
+  
+    "expectedBidResponses": [
+      {
+        "bids": [{
+          "bid": {
+            "id": "1",
+            "impid": "test-imp-id",
+            "price": 1.1,
+            "adm": "some-test-ad",
+            "adid": "17586",
+            "adomain": ["wooga.com"],
+            "cid": "17586",
+            "crid": "17586",
+            "nurl" : "some-test-nurl",
+            "iurl" : "some-test-iurl",
+            "h": 250,
+            "w": 300
+          },
+          "type": "banner"
+        }]
+      }
+    ]
+  }
+  
\ No newline at end of file
diff --git a/adapters/marsmedia/marsmediatest/supplemental/missing-param.json b/adapters/marsmedia/marsmediatest/supplemental/missing-param.json
index b88fba5a..e68b3f52 100644
--- a/adapters/marsmedia/marsmediatest/supplemental/missing-param.json
+++ b/adapters/marsmedia/marsmediatest/supplemental/missing-param.json
@@ -27,7 +27,7 @@
   },
   "expectedMakeRequestsErrors": [
     {
-      "value": "zoneId is empty",
+      "value": "ext.bidder.zoneId not provided",
       "comparison": "literal"
     }
   ]
diff --git a/adapters/marsmedia/params_test.go b/adapters/marsmedia/params_test.go
index 78acd72e..4a91ed6d 100644
--- a/adapters/marsmedia/params_test.go
+++ b/adapters/marsmedia/params_test.go
@@ -41,10 +41,10 @@ func TestInvalidParams(t *testing.T) {
 
 var validParams = []string{
 	`{"zoneId": "9999"}`,
+	`{"zoneId": 9999}`,
 }
 
 var invalidParams = []string{
-	`{"zoneId": 100}`,
 	`{"zoneId": true}`,
 	``,
 	`null`,
diff --git a/adapters/openrtb_util_test.go b/adapters/openrtb_util_test.go
index e09149a7..4a5d33b0 100644
--- a/adapters/openrtb_util_test.go
+++ b/adapters/openrtb_util_test.go
@@ -2,116 +2,100 @@ package adapters
 
 import (
 	"encoding/json"
-	"github.com/mxmCherry/openrtb/v15/openrtb2"
-	"reflect"
+	"errors"
 	"testing"
+
+	"github.com/mxmCherry/openrtb/v15/openrtb2"
+	"github.com/stretchr/testify/assert"
 )
 
-func TestExtractAdapterReqBidderParams(t *testing.T) {
-	type args struct {
-		bidRequest *openrtb2.BidRequest
-	}
+func TestExtractAdapterReqBidderParamsMap(t *testing.T) {
 	tests := []struct {
-		name    string
-		args    args
-		want    map[string]json.RawMessage
-		wantErr bool
+		name            string
+		givenBidRequest *openrtb2.BidRequest
+		want            map[string]json.RawMessage
+		wantErr         error
 	}{
 		{
-			name: "extract bidder params from nil req",
-			args: args{
-				bidRequest: nil,
-			},
-			want:    nil,
-			wantErr: true,
+			name:            "nil req",
+			givenBidRequest: nil,
+			want:            nil,
+			wantErr:         errors.New("error bidRequest should not be nil"),
 		},
 		{
-			name: "extract bidder params from nil req.Ext",
-			args: args{
-				bidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{}}`)},
-			},
-			want:    nil,
-			wantErr: false,
+			name:            "nil req.ext",
+			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{}}`)},
+			want:            nil,
+			wantErr:         nil,
 		},
 		{
-			name: "extract bidder params from req.Ext for input request in adapter code",
-			args: args{
-				bidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"bidderparams": {"profile": 1234, "version": 1}}}`)},
-			},
-			want:    map[string]json.RawMessage{"profile": json.RawMessage(`1234`), "version": json.RawMessage(`1`)},
-			wantErr: false,
+			name:            "malformed req.ext",
+			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage("malformed")},
+			want:            nil,
+			wantErr:         errors.New("error decoding Request.ext : invalid character 'm' looking for beginning of value"),
+		},
+		{
+			name:            "extract bidder params from req.Ext for input request in adapter code",
+			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"bidderparams": {"profile": 1234, "version": 1}}}`)},
+			want:            map[string]json.RawMessage{"profile": json.RawMessage(`1234`), "version": json.RawMessage(`1`)},
+			wantErr:         nil,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := ExtractAdapterReqBidderParams(tt.args.bidRequest)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("ExtractReqExtBidderParams() error = %v, wantErr = %v", err, tt.wantErr)
-				return
-			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("ExtractReqExtBidderParams() got = %v, want = %v", got, tt.want)
-			}
+			got, err := ExtractReqExtBidderParamsMap(tt.givenBidRequest)
+			assert.Equal(t, tt.wantErr, err, "err")
+			assert.Equal(t, tt.want, got, "result")
 		})
 	}
 }
 
-func TestExtractReqExtBidderParams(t *testing.T) {
-	type args struct {
-		request *openrtb2.BidRequest
-	}
+func TestExtractReqExtBidderParamsMap(t *testing.T) {
 	tests := []struct {
-		name    string
-		args    args
-		want    map[string]map[string]json.RawMessage
-		wantErr bool
+		name            string
+		givenBidRequest *openrtb2.BidRequest
+		want            map[string]map[string]json.RawMessage
+		wantErr         error
 	}{
 		{
-			name: "extract bidder params from nil req",
-			args: args{
-				request: nil,
-			},
-			want:    nil,
-			wantErr: true,
+			name:            "nil req",
+			givenBidRequest: nil,
+			want:            nil,
+			wantErr:         errors.New("error bidRequest should not be nil"),
 		},
 		{
-			name: "extract bidder params from nil req.Ext.prebid",
-			args: args{
-				request: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{}}`)},
-			},
-			want:    nil,
-			wantErr: false,
+			name:            "nil req.ext",
+			givenBidRequest: &openrtb2.BidRequest{Ext: nil},
+			want:            nil,
+			wantErr:         nil,
 		},
 		{
-			name: "extract bidder params from nil req.Ext",
-			args: args{
-				request: &openrtb2.BidRequest{Ext: nil},
-			},
-			want:    nil,
-			wantErr: false,
+			name:            "malformed req.ext",
+			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage("malformed")},
+			want:            nil,
+			wantErr:         errors.New("error decoding Request.ext : invalid character 'm' looking for beginning of value"),
 		},
 		{
-			name: "extract bidder params from req.Ext for input request before adapter code",
-			args: args{
-				request: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"bidderparams": {"pubmatic": {"profile": 1234, "version": 1}, "appnexus": {"key1": 123, "key2": {"innerKey1":"innerValue1"} } }}}`)},
-			},
+			name:            "nil req.ext.prebid",
+			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{}}`)},
+			want:            nil,
+			wantErr:         nil,
+		},
+		{
+			name:            "extract bidder params from req.Ext for input request before adapter code",
+			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"bidderparams": {"pubmatic": {"profile": 1234, "version": 1}, "appnexus": {"key1": 123, "key2": {"innerKey1":"innerValue1"} } }}}`)},
 			want: map[string]map[string]json.RawMessage{
 				"pubmatic": {"profile": json.RawMessage(`1234`), "version": json.RawMessage(`1`)},
 				"appnexus": {"key1": json.RawMessage(`123`), "key2": json.RawMessage(`{"innerKey1":"innerValue1"}`)},
 			},
-			wantErr: false,
+			wantErr: nil,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := ExtractReqExtBidderParams(tt.args.request)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("ExtractReqExtBidderParams() error = %v, wantErr = %v", err, tt.wantErr)
-				return
-			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("ExtractReqExtBidderParams() got = %v, want = %v", got, tt.want)
-			}
+			got, err := ExtractReqExtBidderParamsEmbeddedMap(tt.givenBidRequest)
+			assert.Equal(t, tt.wantErr, err, "err")
+			assert.Equal(t, tt.want, got, "result")
 		})
 	}
 }
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index af68d693..a25bb406 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -1146,6 +1146,301 @@ func TestStoredRequests(t *testing.T) {
 	}
 }
 
+func TestMergeBidderParams(t *testing.T) {
+	testCases := []struct {
+		description         string
+		givenRequest        openrtb2.BidRequest
+		expectedRequestImps []openrtb2.Imp
+	}{
+		{
+			description: "No Request Params",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1}}`)}},
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1}}`)}},
+		},
+		{
+			description: "No Request Params - Empty Object",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1}}`)}},
+		},
+		{
+			description: "Malformed Request Params",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":malformed}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1}}`)}},
+		},
+		{
+			description: "No Imps",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{},
+		},
+		{
+			description: "One Imp - imp.ext Modified",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1,"b":2}}`)}},
+		},
+		{
+			description: "One Imp - imp.ext.prebid.bidder Modified",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidder1":{"a":1}}}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidder1":{"a":1,"b":2}}}}`)}},
+		},
+		{
+			description: "One Imp - imp.ext + imp.ext.prebid.bidder Modified - Different Bidders",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1},"prebid":{"bidder":{"bidder2":{"a":"one"}}}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2},"bidder2":{"b":"two"}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1,"b":2},"prebid":{"bidder":{"bidder2":{"a":"one","b":"two"}}}}`)}},
+		},
+		{
+			description: "One Imp - imp.ext + imp.ext.prebid.bidder Modified - Same Bidder",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1},"prebid":{"bidder":{"bidder1":{"a":"one"}}}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1,"b":2},"prebid":{"bidder":{"bidder1":{"a":"one","b":2}}}}`)}},
+		},
+		{
+			description: "One Imp - No imp.ext",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{{ID: "1"}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{{ID: "1"}},
+		},
+		{
+			description: "Multiple Imps - Modified Mixed",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{
+					{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1},"prebid":{"bidder":{"bidder1":{"a":"one"}}}}`)},
+					{ID: "2", Ext: json.RawMessage(`{"bidder2":{"a":1,"b":"existing"},"prebid":{"bidder":{"bidder2":{"a":"one"}}}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2},"bidder2":{"b":"two"}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{
+				{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1,"b":2},"prebid":{"bidder":{"bidder1":{"a":"one","b":2}}}}`)},
+				{ID: "2", Ext: json.RawMessage(`{"bidder2":{"a":1,"b":"existing"},"prebid":{"bidder":{"bidder2":{"a":"one","b":"two"}}}}`)}},
+		},
+		{
+			description: "Multiple Imps - None Modified Mixed",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{
+					{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1},"prebid":{"bidder":{"bidder2":{"a":"one"}}}}`)},
+					{ID: "2", Ext: json.RawMessage(`{"bidder1":{"a":2},"prebid":{"bidder":{"bidder2":{"a":"two"}}}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder3":{"c":3}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{
+				{ID: "1", Ext: json.RawMessage(`{"bidder1":{"a":1},"prebid":{"bidder":{"bidder2":{"a":"one"}}}}`)},
+				{ID: "2", Ext: json.RawMessage(`{"bidder1":{"a":2},"prebid":{"bidder":{"bidder2":{"a":"two"}}}}`)}},
+		},
+		{
+			description: "Multiple Imps - One Malformed",
+			givenRequest: openrtb2.BidRequest{
+				Imp: []openrtb2.Imp{
+					{ID: "1", Ext: json.RawMessage(`malformed`)},
+					{ID: "2", Ext: json.RawMessage(`{"bidder2":{"a":1,"b":"existing"},"prebid":{"bidder":{"bidder2":{"a":"one"}}}}`)}},
+				Ext: json.RawMessage(`{"prebid":{"bidderparams":{"bidder1":{"b":2},"bidder2":{"b":"two"}}}}`),
+			},
+			expectedRequestImps: []openrtb2.Imp{
+				{ID: "1", Ext: json.RawMessage(`malformed`)},
+				{ID: "2", Ext: json.RawMessage(`{"bidder2":{"a":1,"b":"existing"},"prebid":{"bidder":{"bidder2":{"a":"one","b":"two"}}}}`)}},
+		},
+	}
+
+	for _, test := range testCases {
+		actualErr := mergeBidderParams(&openrtb_ext.RequestWrapper{BidRequest: &test.givenRequest})
+
+		// errors are only possible from the marshal operation, which is not testable
+		assert.NoError(t, actualErr, test.description+":err")
+
+		assert.Equal(t, test.givenRequest.Imp, test.expectedRequestImps, test.description+":imps")
+	}
+}
+
+func TestAddMissingReqExtParamsInImpExtPrebid(t *testing.T) {
+	testCases := []struct {
+		description            string
+		givenImpExtByBidder    map[string]json.RawMessage
+		givenReqExtParams      map[string]map[string]json.RawMessage
+		expectedModified       bool
+		expectedImpExtByBidder map[string]json.RawMessage
+	}{
+		{
+			description:            "No Prebid Section",
+			givenImpExtByBidder:    map[string]json.RawMessage{},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{},
+		},
+		{
+			description:            "Malformed Prebid Section",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`malformed`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`malformed`)},
+		},
+		{
+			description:            "No Prebid Bidder Section",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{}`)},
+		},
+		{
+			description:            "Malformed Prebid Bidder Section",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder": malformed}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder": malformed}`)},
+		},
+		{
+			description:            "One Bidder - Modified (no collision)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1}}}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       true,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1,"b":2}}}`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (imp.ext wins)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1,"b":2}}}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`4`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1,"b":2}}}`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (reserved bidder ignored)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"gpid":{"a":1}}}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"gpid": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"gpid":{"a":1}}}`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (reserved bidder ignored - not embedded object)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"gpid":1}}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"gpid": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"gpid":1}}`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (malformed ignored)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":malformed}}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":malformed}}`)},
+		},
+		{
+			description:            "Multiple Bidders - Mixed",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1},"bidder2":{"a":"one","b":"two"}}}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}, "bidder2": {"b": json.RawMessage(`"three"`)}},
+			expectedModified:       true,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1,"b":2},"bidder2":{"a":"one","b":"two"}}}`)},
+		},
+		{
+			description:            "Multiple Bidders - None Modified",
+			givenImpExtByBidder:    map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1},"bidder2":{"a":"one","b":"two"}}}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"prebid": json.RawMessage(`{"bidder":{"bidder1":{"a":1},"bidder2":{"a":"one","b":"two"}}}`)},
+		},
+	}
+
+	for _, test := range testCases {
+		actualModified, actualErr := addMissingReqExtParamsInImpExtPrebid(test.givenImpExtByBidder, test.givenReqExtParams)
+
+		// errors are only possible from the marshal operation, which is not testable
+		assert.NoError(t, actualErr, test.description+":err")
+
+		assert.Equal(t, test.expectedModified, actualModified, test.description+":modified")
+		assert.Equal(t, test.expectedImpExtByBidder, test.givenImpExtByBidder, test.description+":imp.ext")
+	}
+}
+
+func TestAddMissingReqExtParamsInImpExt(t *testing.T) {
+	testCases := []struct {
+		description            string
+		givenImpExtByBidder    map[string]json.RawMessage
+		givenReqExtParams      map[string]map[string]json.RawMessage
+		expectedModified       bool
+		expectedImpExtByBidder map[string]json.RawMessage
+	}{
+		{
+			description:            "One Bidder - Modified (no collision)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       true,
+			expectedImpExtByBidder: map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1,"b":2}`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (imp.ext wins)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1,"b":2}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`4`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1,"b":2}`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (reserved bidder ignored)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"gpid": json.RawMessage(`{"a":1}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"gpid": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"gpid": json.RawMessage(`{"a":1}`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (reserved bidder ignored - not embedded object)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"gpid": json.RawMessage(`1`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"gpid": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"gpid": json.RawMessage(`1`)},
+		},
+		{
+			description:            "One Bidder - Not Modified (malformed ignored)",
+			givenImpExtByBidder:    map[string]json.RawMessage{"bidder1": json.RawMessage(`malformed`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"bidder1": json.RawMessage(`malformed`)},
+		},
+		{
+			description:            "Multiple Bidders - Mixed",
+			givenImpExtByBidder:    map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1}`), "bidder2": json.RawMessage(`{"a":"one","b":"two"}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{"bidder1": {"b": json.RawMessage(`2`)}, "bidder2": {"b": json.RawMessage(`"three"`)}},
+			expectedModified:       true,
+			expectedImpExtByBidder: map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1,"b":2}`), "bidder2": json.RawMessage(`{"a":"one","b":"two"}`)},
+		},
+		{
+			description:            "Multiple Bidders - None Modified",
+			givenImpExtByBidder:    map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1}`), "bidder2": json.RawMessage(`{"a":"one","b":"two"}`)},
+			givenReqExtParams:      map[string]map[string]json.RawMessage{},
+			expectedModified:       false,
+			expectedImpExtByBidder: map[string]json.RawMessage{"bidder1": json.RawMessage(`{"a":1}`), "bidder2": json.RawMessage(`{"a":"one","b":"two"}`)},
+		},
+	}
+
+	for _, test := range testCases {
+		actualModified, actualErr := addMissingReqExtParamsInImpExt(test.givenImpExtByBidder, test.givenReqExtParams)
+
+		// errors are only possible from the marshal operation, which is not testable
+		assert.NoError(t, actualErr, test.description+":err")
+
+		assert.Equal(t, test.expectedModified, actualModified, test.description+":modified")
+		assert.Equal(t, test.expectedImpExtByBidder, test.givenImpExtByBidder, test.description+":imp.ext")
+	}
+}
+
+//impExtByBidder map[string]json.RawMessage, reqExtParams map[string]map[string]json.RawMessage) (bool, error) {
+
 func TestValidateRequest(t *testing.T) {
 	deps := &endpointDeps{
 		fakeUUIDGenerator{},
@@ -4048,51 +4343,33 @@ func TestValidateBanner(t *testing.T) {
 }
 
 func TestParseRequestMergeBidderParams(t *testing.T) {
-	type args struct {
-		reqBody string
-	}
 	tests := []struct {
-		name           string
-		args           args
-		expectedImpExt json.RawMessage
-		expectedReqExt json.RawMessage
-		errL           int
+		name               string
+		givenRequestBody   string
+		expectedImpExt     json.RawMessage
+		expectedReqExt     json.RawMessage
+		expectedErrorCount int
 	}{
 		{
-			name: "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext.prebid.bidder",
-			args: args{
-				reqBody: validRequest(t, "req-ext-bidder-params.json"),
-			},
-			expectedImpExt: getObject(t, "req-ext-bidder-params.json", "expectedImpExt"),
-			expectedReqExt: getObject(t, "req-ext-bidder-params.json", "expectedReqExt"),
-			errL:           0,
+			name:               "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext.prebid.bidder",
+			givenRequestBody:   validRequest(t, "req-ext-bidder-params.json"),
+			expectedImpExt:     getObject(t, "req-ext-bidder-params.json", "expectedImpExt"),
+			expectedReqExt:     getObject(t, "req-ext-bidder-params.json", "expectedReqExt"),
+			expectedErrorCount: 0,
 		},
 		{
-			name: "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext.prebid.bidder with preference for imp[].ext.prebid.bidder params",
-			args: args{
-				reqBody: validRequest(t, "req-ext-bidder-params-merge.json"),
-			},
-			expectedImpExt: getObject(t, "req-ext-bidder-params-merge.json", "expectedImpExt"),
-			expectedReqExt: getObject(t, "req-ext-bidder-params-merge.json", "expectedReqExt"),
-			errL:           0,
+			name:               "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext.prebid.bidder with preference for imp[].ext.prebid.bidder params",
+			givenRequestBody:   validRequest(t, "req-ext-bidder-params-merge.json"),
+			expectedImpExt:     getObject(t, "req-ext-bidder-params-merge.json", "expectedImpExt"),
+			expectedReqExt:     getObject(t, "req-ext-bidder-params-merge.json", "expectedReqExt"),
+			expectedErrorCount: 0,
 		},
 		{
-			name: "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext for backward compatibility",
-			args: args{
-				reqBody: validRequest(t, "req-ext-bidder-params-backward-compatible-merge.json"),
-			},
-			expectedImpExt: getObject(t, "req-ext-bidder-params-backward-compatible-merge.json", "expectedImpExt"),
-			expectedReqExt: getObject(t, "req-ext-bidder-params-backward-compatible-merge.json", "expectedReqExt"),
-			errL:           0,
-		},
-		{
-			name: "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext.prebid.bidder for reserved keyword",
-			args: args{
-				reqBody: validRequest(t, "req-ext-bidder-params-merge-reserved-keyword.json"),
-			},
-			expectedImpExt: getObject(t, "req-ext-bidder-params-merge-reserved-keyword.json", "expectedImpExt"),
-			expectedReqExt: getObject(t, "req-ext-bidder-params-merge-reserved-keyword.json", "expectedReqExt"),
-			errL:           0,
+			name:               "add missing bidder-params from req.ext.prebid.bidderparams to imp[].ext for backward compatibility",
+			givenRequestBody:   validRequest(t, "req-ext-bidder-params-backward-compatible-merge.json"),
+			expectedImpExt:     getObject(t, "req-ext-bidder-params-backward-compatible-merge.json", "expectedImpExt"),
+			expectedReqExt:     getObject(t, "req-ext-bidder-params-backward-compatible-merge.json", "expectedReqExt"),
+			expectedErrorCount: 0,
 		},
 	}
 	for _, tt := range tests {
@@ -4105,7 +4382,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) {
 				&mockStoredReqFetcher{},
 				empty_fetcher.EmptyFetcher{},
 				empty_fetcher.EmptyFetcher{},
-				&config.Configuration{MaxRequestSize: int64(len(tt.args.reqBody))},
+				&config.Configuration{MaxRequestSize: int64(len(tt.givenRequestBody))},
 				&metricsConfig.NilMetricsEngine{},
 				analyticsConf.NewPBSAnalytics(&config.Analytics{}),
 				map[string]string{},
@@ -4117,7 +4394,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) {
 				hardcodedResponseIPValidator{response: true},
 			}
 
-			req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(tt.args.reqBody))
+			req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(tt.givenRequestBody))
 
 			resReq, _, errL := deps.parseRequest(req)
 
@@ -4140,7 +4417,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) {
 
 			assert.Equal(t, eReqE, reqE, "req.Ext should match")
 
-			assert.Len(t, errL, tt.errL, "error length should match")
+			assert.Len(t, errL, tt.expectedErrorCount, "error length should match")
 		})
 	}
 }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index d0b4006d..c10e294f 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -249,6 +249,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			givenImpExtPrebid: map[string]json.RawMessage{
 				"bidder": json.RawMessage(`"anyBidder"`),
@@ -257,6 +258,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			expectedError: "",
 		},
@@ -267,6 +269,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			givenImpExtPrebid: map[string]json.RawMessage{
 				"bidder":    json.RawMessage(`"anyBidder"`),
@@ -277,6 +280,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			expectedError: "",
 		},
@@ -287,12 +291,14 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":      json.RawMessage(`"anyData"`),
 				"context":   json.RawMessage(`"anyContext"`),
 				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":      json.RawMessage(`"anyGPID"`),
 			},
 			givenImpExtPrebid: map[string]json.RawMessage{},
 			expected: map[string]json.RawMessage{
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			expectedError: "",
 		},
@@ -304,6 +310,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":      json.RawMessage(`"anyData"`),
 				"context":   json.RawMessage(`"anyContext"`),
 				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":      json.RawMessage(`"anyGPID"`),
 			},
 			givenImpExtPrebid: map[string]json.RawMessage{
 				"bidder": json.RawMessage(`"anyBidder"`),
@@ -312,6 +319,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			expectedError: "",
 		},
@@ -323,6 +331,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":      json.RawMessage(`"anyData"`),
 				"context":   json.RawMessage(`"anyContext"`),
 				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":      json.RawMessage(`"anyGPID"`),
 			},
 			givenImpExtPrebid: map[string]json.RawMessage{
 				"bidder":    json.RawMessage(`"anyBidder"`),
@@ -333,6 +342,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			expectedError: "",
 		},
@@ -343,6 +353,7 @@ func TestCreateSanitizedImpExt(t *testing.T) {
 				"data":    json.RawMessage(`"anyData"`),
 				"context": json.RawMessage(`"anyContext"`),
 				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
+				"gpid":    json.RawMessage(`"anyGPID"`),
 			},
 			givenImpExtPrebid: map[string]json.RawMessage{
 				"malformed": json.RawMessage(`json`), // String value without quotes.
@@ -401,7 +412,7 @@ func TestExtractBidderExts(t *testing.T) {
 		},
 		{
 			description:              "Special Names Ignored - imp.ext.BIDDER",
-			givenImpExt:              map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`)},
+			givenImpExt:              map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`), "gpid": json.RawMessage(`{"gpid":"value4"}}`)},
 			givenImpExtPrebidBidders: map[string]json.RawMessage{},
 			expected:                 map[string]json.RawMessage{},
 		},
@@ -420,8 +431,8 @@ func TestExtractBidderExts(t *testing.T) {
 		{
 			description:              "Special Names Not Treated Differently - imp.ext.prebid.bidder.BIDDER",
 			givenImpExt:              map[string]json.RawMessage{},
-			givenImpExtPrebidBidders: map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`)},
-			expected:                 map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`)},
+			givenImpExtPrebidBidders: map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`), "gpid": json.RawMessage(`{"gpid":"value4"}}`)},
+			expected:                 map[string]json.RawMessage{"prebid": json.RawMessage(`{"prebid":"value1"}}`), "context": json.RawMessage(`{"firstPartyData":"value2"}}`), "skadn": json.RawMessage(`{"skAdNetwork":"value3"}}`), "gpid": json.RawMessage(`{"gpid":"value4"}}`)},
 		},
 		{
 			description:              "Mixed - Both - imp.ext.BIDDER + imp.ext.prebid.bidder.BIDDER",
diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go
index fc8abdb9..c7b293da 100644
--- a/metrics/prometheus/prometheus_test.go
+++ b/metrics/prometheus/prometheus_test.go
@@ -26,7 +26,7 @@ func TestMetricCountGatekeeping(t *testing.T) {
 	m := createMetricsForTesting()
 
 	// Gather All Metrics
-	metricFamilies, err := m.Registry.Gather()
+	metricFamilies, err := m.Gatherer.Gather()
 	assert.NoError(t, err, "gather metics")
 
 	// Summarize By Adapter Cardinality
diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go
index 8bd25d9e..cde79f70 100644
--- a/openrtb_ext/bidders_test.go
+++ b/openrtb_ext/bidders_test.go
@@ -104,15 +104,15 @@ func TestIsBidderNameReserved(t *testing.T) {
 		{"general", true},
 		{"gEnErAl", true},
 		{"GENERAL", true},
-		{"skadn", true},
-		{"skADN", true},
-		{"SKADN", true},
+		{"gpid", true},
+		{"GPID", true},
+		{"GPid", true},
 		{"prebid", true},
 		{"PREbid", true},
 		{"PREBID", true},
-		{"bidder", true},
-		{"BIDDER", true},
-		{"BidDer", true},
+		{"skadn", true},
+		{"skADN", true},
+		{"SKADN", true},
 		{"notreserved", false},
 	}