Overview of the Loyalty Customer Match Service

This guide describes how to use the Loyalty Customer Match Service in the Merchant API. This service enables merchants to manage customer loyalty data, such as user identifiers and tier information, for organic personalization on Google Search, without requiring an active Google Ads account.

Overview

Use the Loyalty Customer Match Service to upload loyalty data, which is then used to provide organic loyalty personalization features on Google Search, such as displaying member-specific pricing. You use the ManageLoyaltyCustomerMatch custom method to associate your customers with loyalty program tiers, enabling you to insert, update, or remove their loyalty status based on user identifiers.

Key concepts

  • Unified interface: A unique endpoint to add, update, or remove customer loyalty tier details.
  • Privacy-first design: To protect user privacy and prevent unauthorized account probing, the API does not support GET or LIST operations, ensuring that data is managed without retrieval or auditing.
  • Flexible identification: Match users using at least one valid identifier, such as an email address, physical address, or phone number.
  • Consent-based processing: The service stores and uses customer data only when the end-user has granted the necessary consent to Google. To protect and prevent account existence probing or consent status, the service returns a silent success if a match is not made or consent is not granted.

Prerequisites

Follow these requirements to use the Loyalty Customer Match Service:

  • Account setup: Ensure you have an active Merchant Center account. You don't need to create a Google Ads account to use the Loyalty Customer Match Service.
  • Loyalty program configuration: Enable the loyalty program in your Merchant Center account, and make sure you have defined loyalty tiers.
  • Tier order awareness: Be aware of the order in which your loyalty tiers are defined in the Merchant Center UI. The API uses this exact sequence for its enum mapping.

Method: ManageLoyaltyCustomerMatch

The ManageLoyaltyCustomerMatch method serves as the central interface for managing customer loyalty associations. Based on the input provided, the service automatically determines whether to insert, update, or remove the loyalty tier status of a customer. The operation is idempotent: repeated identical requests have the same effect as a single request.

The following request demonstrates how to manage customer loyalty associations through the API:

POST https://merchantapi.googleapis.com/{api_version}/accounts/{account_id}/loyaltyCustomers:manage

This request defines the following required path parameters:

  • api_version: The API version such as v1.
  • account_id: The Merchant Center account ID.

Include a loyaltyCustomer object in the request body.

{
    "userIdentifier": {
      "emailAddress": "string",
      "address": {
        "addressLines": ["string"],
        "locality": "string",
        "administrativeArea": "string",
        "postalCode": "string",
        "regionCode": "string"
      },
      "phoneNumber": "string"
    },
    "loyaltyTier": "LoyaltyTier",
    "pointBalance": "integer"
  }

loyaltyCustomer fields

  • userIdentifier: The set of identifiers used to match the customer. At least one field within userIdentifier must be provided and valid.
  • loyaltyTier: The loyalty tier to associate with the customer. Maps to the order of the tier in the Merchant Center setup. For details, see Understanding loyaltyTier mapping. Use NON_MEMBER to remove an existing association.
  • pointBalance: The customer's current points balance.

userIdentifier fields

At least one of the following fields must be provided:

  • emailAddress: The customer's email address.
  • address: The customer's physical address. PostalCode is required.
  • phoneNumber: The customer's phone number. E.164 format is recommended.

Understand loyaltyTier mapping

The API does not use the custom names. The loyaltyTier enum values (TIER1 to TIER7) are semantic labels. They don't use the custom names (for example, "Gold Rewards") or custom labels (for example, "gold_tier") you assigned in your Merchant Center UI. Instead, they map strictly to the order in which you defined your tiers in the loyalty program settings in the Merchant Center:

  • TIER1: Corresponds to the first tier listed in your Merchant Center loyalty program configuration.
  • TIER2: Corresponds to the second tier listed in your Merchant Center loyalty program configuration.
  • TIER3 through TIER7: Correspond to the third through seventh tiers listed in your Merchant Center loyalty program configuration.

Example:

If your Merchant Center loyalty program has tiers defined in this order:

  1. Tier Name: "Silver Status", Tier Label: "silver"
  2. Tier Name: "Gold Member", Tier Label: "gold"
  3. Tier Name: "Platinum Elite", Tier Label: "platinum"

Then, in accounts.loyaltyCustomers.manage API calls:

  • To assign a customer to "Silver Status", you must use loyaltyTier: TIER1.
  • To assign a customer to "Gold Member", you must use loyaltyTier: TIER2.
  • To assign a customer to "Platinum Elite", you must use loyaltyTier: TIER3.

LoyaltyTier enum values

  • TIER1
  • TIER2
  • TIER3
  • TIER4
  • TIER5
  • TIER6
  • TIER7
  • NON_MEMBER (Used to signal removal of the customer's loyalty association)

Understand the ManageLoyaltyCustomerMatch response body

The ManageLoyaltyCustomerMatch method returns a ManageLoyaltyCustomerMatchResponse object:

{
  "loyaltyCustomer": {
    // loyaltyCustomer object from the request
  }
}

Important considerations on possible responses:

  • Successful upsert (data stored): To successfully store or update a customer's loyalty tier association, meet the following conditions:

    • you match a Google user with the provided userIdentifier
    • you set the loyaltyTier in the request to a valid value other than NON_MEMBER
    • the matched user has consented to loyalty data usage

The response contains the loyaltyCustomer object from your request, indicating the data is successfully processed and stored:

{
  "loyaltyCustomer": {
    "userIdentifier": {
     "emailAddress": "customer@example.com"
    },
    "loyaltyTier": "TIER2",
    "pointBalance": 1500
    }
}
  • Successful deletion: To successfully remove any existing loyalty association for the customer with this merchant, the following conditions must be fulfilled:
    • you match a Google user with the provided userIdentifier
    • you set the loyaltyTier in the request to NON_MEMBER

The response is an empty JSON object:

{}
  • No match / no consent (silent success): If the provided userIdentifier does not match a Google Account, or if the matched user has not consented to loyalty data usage, the API returns an HTTP 200 OK status with an empty JSON object: {}. This happens for both upsert and removal attempts.

Examples

TIER1 corresponds to the first defined tier of the merchant, the one called "Basic", and TIER2 to their second - "Premium"

To add a customer to TIER2 or update their status using an email address, send the following request:

POST
"https://merchantapi.googleapis.com/v1/accounts/{account_id}/loyaltyCustomers:manage"
  -d '{
      "userIdentifier": {
        "emailAddress": "customer@example.com"
      },
      "loyaltyTier": "TIER2",
      "pointBalance": 1500
  }'

When a user is successfully matched and has consented, the API returns the following response:

{
  "loyaltyCustomer": {
    "userIdentifier": {
      "emailAddress": "customer@example.com"
    },
    "loyaltyTier": "TIER2",
    "pointBalance": 1500
  }
}

When there is no match or the user hasn't consented, the API returns the following response:

{}

To remove a customer's loyalty association using a phone number, send the following request:

POST
"https://merchantapi.googleapis.com/v1/accounts/{account_id}/loyaltyCustomers:manage" \
  -d '{
      "userIdentifier": {
        "phoneNumber": "+18005550132"
      },
      "loyaltyTier": "NON_MEMBER"
  }'

Regardless of whether a record existed, the API returns the following success response:

{}

To add or update a customer using multiple identifiers, send the following request:

POST
"https://merchantapi.googleapis.com/v1/accounts/{account_id}/loyaltyCustomers:manage" \
  -d '{
      "userIdentifier": {
        "emailAddress": "user@example.com",
        "address": {
          "postalCode": "94043",
          "regionCode": "US"
        }
      },
      "loyaltyTier": "TIER1"
  }'

Response is similar to the first example, depending on match and consent.

Error handling

The API uses standard HTTP codes. Common error strings include:

HTTP code Error String Description
400 INVALID_ARGUMENT Missing user_identifier or loyalty_tier, or identifier is empty.
401 UNAUTHENTICATED Invalid or missing credentials.
403 PERMISSION_DENIED The authenticated user does not have access to the specified Merchant Center account.
404 NOT_FOUND The specified loyalty tier label does not exist in your configuration.
412 FAILED_PRECONDITION You have not configured a loyalty program in your account.
429 RESOURCE_EXHAUSTED Quota limit reached.

Error examples

Example for 404 NOT_FOUND:

Any valid request to an account ID that does not have a loyalty program configured.

The API returns the following error response:

{
  "error": {
    "code": 404,
    "message": "The loyalty program is not found for account: {account_id}.",
    "status": "NOT_FOUND",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "notFound",
        "domain": "merchantapi.googleapis.com",
        "metadata": {
          "ACCOUNT_ID": "{account_id}",
          "REASON": "NOT_FOUND_LOYALTY_PROGRAM"
        }
      }
    ]
  }
}

Reason: The merchant account in the path does not have an active loyalty program.

Examples of 400 INVALID_ARGUMENT:

An error occurs if the request contains an invalid value for the loyaltyTier field:

{
  "loyaltyCustomer": {
    "userIdentifier": {
      "emailAddress": "customer@example.com"
    },
    "loyaltyTier": "TIER11",
    "pointBalance": 100
  }
}

The API returns the following error response:

{
  "error": {
    "code": 400,
    "message": "Invalid value at 'loyalty_customer.loyalty_tier' (type.googleapis.com/google.shopping.merchant.loyaltycustomers.v1.LoyaltyCustomer.LoyaltyTier), \"TIER11\"",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "loyalty_customer.loyalty_tier",
            "description": "Invalid value at 'loyalty_customer.loyalty_tier' (type.googleapis.com/google.shopping.merchant.loyaltycustomers.v1.LoyaltyCustomer.LoyaltyTier), \"TIER11\""
          }
        ]
      }
    ]
  }
}

Reason: TIER11 is not a valid enum value for loyaltyTier. The same error can happen when you try to specify TIER2 when there's only one tier available.

An error occurs if the required loyaltyTier field is missing from the request body:

{
  "loyaltyCustomer": {
    "userIdentifier": {
      "emailAddress": "customer@example.com"
    },
    "pointBalance": 100
  }
}

The API returns the following error response:

{
  "error": {
    "code": 400,
    "message": "[loyalty_customer.loyalty_tier] Required field not provided: loyalty_customer.loyalty_tier",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "required",
        "domain": "merchantapi.googleapis.com",
        "metadata": {
          "FIELD_LOCATION": "loyalty_customer.loyalty_tier",
          "REASON": "MISSING_REQUIRED_FIELD"
        }
      }
    ]
  }
}

Reason: The loyaltyTier field is required.

An error occurs if an address identifier is incomplete, such as when the postalCode field is missing:

{
  "loyaltyCustomer": {
    "userIdentifier": {
      "address": {
        "locality": "Sunnyvale",
        "administrativeArea": "CA",
        "regionCode": "US"
      }
    },
    "loyaltyTier": "TIER1",
    "pointBalance": 100
  }
}

The API returns the following error response:

{
  "error": {
    "code": 400,
    "message": "[loyalty_customer.user_identifier] The format of loyalty_customer.user_identifier does not match the expected format ... Value: at least one valid user identifier should be provided.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "invalid",
        "domain": "merchantapi.googleapis.com",
        "metadata": {
          "FIELD_NAME": "loyalty_customer.user_identifier",
          "REASON": "INVALID_VALUE"
        }
      }
    ]
  }
}

Reason: An address is provided, but it's missing the required postalCode field, so it's not considered a valid identifier.

An error occurs if you request a tier index that is out of bounds for the configured program:

Scenario: The merchant has only one tier configured in the Merchant Center.

{
  "loyaltyCustomer": {
    "userIdentifier": {
      "emailAddress": "customer@example.com"
    },
    "loyaltyTier": "TIER2",
    "pointBalance": 100
  }
}

The API returns the following error response:

{
  "error": {
    "code": 400,
    "message": "[loyalty_customer.loyalty_tier] The format of loyalty_customer.loyalty_tier does not match the expected format `valid LoyaltyTier`. Value: TIER2.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "invalid",
        "domain": "merchantapi.googleapis.com",
        "metadata": {
          "FIELD_NAME": "loyalty_customer.loyalty_tier",
          "PATTERN": "valid LoyaltyTier",
          "FIELD_VALUE": "TIER2",
          "REASON": "INVALID_VALUE"
        }
      }
    ]
  }
}

Reason: TIER2 is requested, but the loyalty program linked to the account doesn't have a second tier defined.

An error occurs if the request contains a malformed emailAddress:

{
  "loyaltyCustomer": {
    "userIdentifier": {
      "emailAddress": "customer@google"
    },
    "loyaltyTier": "TIER1",
    "pointBalance": 100
  }
}

The API returns the following error response:

{
  "error": {
    "code": 400,
    "message": "[loyalty_customer.user_identifier] The format of loyalty_customer.user_identifier does not match the expected format `email_address: \t \"customer@google\"\n`. Value: at least one valid user identifier should be provided.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "invalid",
        "domain": "merchantapi.googleapis.com",
        "metadata": {
          "FIELD_NAME": "loyalty_customer.user_identifier",
          "REASON": "INVALID_VALUE"
        }
      }
    ]
  }
}

Reason: The email address format is invalid.

An error occurs if the userIdentifier object is empty:

{
  "loyaltyCustomer": {
    "userIdentifier": {},
    "loyaltyTier": "TIER1",
    "pointBalance": 100
  }
}

The API returns the following error response:

{
  "error": {
    "code": 400,
    "message": "[loyalty_customer.user_identifier] Required field not provided: loyalty_customer.user_identifier",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "required",
        "domain": "merchantapi.googleapis.com",
        "metadata": {
          "FIELD_LOCATION": "loyalty_customer.user_identifier",
          "REASON": "MISSING_REQUIRED_FIELD"
        }
      }
    ]
  }
}

Reason: The userIdentifier object is present but contains no actual identifier fields.

Note on identifier validation:

  • The API performs basic format checks on identifiers (for example, email structure, presence of postalCode in addresses).
  • However, some identifiers that pass initial checks might not match any Google user account or may not be in a format recognized by the backend matching system. In such cases, you will receive the silent success empty response {} with HTTP status 200 OK.

Best practices

Follow these best practices to optimize your integration.

  • For large-scale integration: Because the API operates on a per-request basis, client-side parallelism is required to achieve the necessary throughput for large datasets. You should design your integration to manage multiple concurrent requests. For guidance on how to structure your implementation to handle higher volumes through parallelization, refer to our guide on how to send multiple requests.

  • Quota management: The default quota is 1,000,000 requests/day and 10,000 requests/minute. To see how you can monitor and check your quotas, see Quotas and limits.

  • Prioritize email address: Whenever possible, include the customer's emailAddress in the userIdentifier. Email addresses are generally the most accurate and reliable identifier for matching users to their Google Accounts.

  • Handle empty responses: Design your application to correctly interpret empty {} responses as a success, understanding that it means the data was not stored due to privacy reasons (no match or no consent). Don't retry the request.

  • Verify tier order: Always confirm the order of your loyalty tiers in the Merchant Center UI to ensure you are using the correct TIER1 through TIER7 enum values in your API calls. This mapping is based on the defined order in the UI, not their names.

  • Monitor errors: Log and monitor API responses, paying attention to any 4xx errors to catch integration issues, especially 404 errors which might indicate a mismatch in tier understanding.