Tipps zur Leistungsoptimierung

In diesem Dokument werden einige Techniken beschrieben, mit denen du die Leistung deiner Anwendung verbessern kannst. In einigen Fällen werden Beispiele aus anderen APIs oder generischen APIs verwendet, um die vorgestellten Ideen zu erläutern. Für die Google Wallet API gelten jedoch dieselben Konzepte.

Komprimierung mit gzip

Durch Aktivierung der gzip-Komprimierung kann die für jede Anfrage benötigte Bandbreite einfach und bequem reduziert werden. Auch wenn die Dekomprimierung der Ergebnisse zusätzliche CPU-Zeit kostet, lohnt sie sich verglichen mit den Netzwerkkosten durchaus.

Du musst zwei Schritte ausführen, um eine mit gzip codierte Antwort zu erhalten: Lege einen Header Accept-Encoding fest und ändere den User-Agent so, dass er den String gzip enthält. Hier ein Beispiel für HTTP-Header im korrekten Format zur Aktivierung der gzip-Komprimierung:

Accept-Encoding: gzip
User-Agent: my program (gzip)

Mit Teilressourcen arbeiten

Eine andere Möglichkeit zur Verbesserung der Leistung der API-Aufrufe besteht darin, nur den Teil der Daten zu senden und zu empfangen, der für dich interessant ist. Dadurch entfallen das Übertragen, Parsen und Speichern nicht benötigter Felder, sodass die Ressourcen wie das Netzwerk, die CPU und der Speicher effizient eingesetzt werden.

Es gibt zwei Arten von Teilanfragen:

  • Teilantwort: Eine Anfrage, mit der du angibst, welche Felder in der Antwort enthalten sein sollen. Verwende dazu den Anfrageparameter fields.
  • Patch: Eine Aktualisierungsanfrage, bei der du nur die zu ändernden Felder sendest. Verwende dazu das HTTP-Verb PATCH.

In den folgenden Abschnitten finden Sie weitere Informationen zu Teilanfragen.

Teilantwort

Standardmäßig wird nach der Verarbeitung einer Anfrage die komplette Darstellung einer Ressource vom Server zurückgeliefert. Du kannst den Server zwecks Leistungsverbesserung aber auch anweisen, nur die Felder zu senden, die du wirklich benötigst, und erhältst dann eine Teilantwort.

Verwende zum Anfragen einer Teilantwort den Anfrageparameter fields. Damit kannst du angeben, welche Felder zurückgegeben werden sollen. Du kannst diesen Parameter mit jeder beliebigen Anfrage verwenden, die Antwortdaten zurückgibt.

Beachte, dass sich der fields-Parameter nur auf die Antwortdaten auswirkt. Er wirkt sich nicht auf die Daten aus, die du gegebenenfalls senden musst. Verwende eine Patchanfrage, um die Datenmenge zu reduzieren, die bei der Änderung von Ressourcen gesendet wird.

Beispiel

Das folgende Beispiel zeigt die Verwendung des fields-Parameters mit einer allgemeinen (fiktiven) „Demo“-API.

Einfache Anfrage: Bei dieser HTTP-GET-Anfrage wird der Parameter fields weggelassen und die vollständige Ressource zurückgegeben.

https://www.googleapis.com/demo/v1

Antwort mit vollständiger Ressource: Die vollständigen Ressourcendaten umfassen folgende Felder sowie zahlreiche weitere Felder, die der Übersichtlichkeit halber ausgelassen wurden.

{
  "kind": "demo",
  ...
  "items": [
  {
    "title": "First title",
    "comment": "First comment.",
    "characteristics": {
      "length": "short",
      "accuracy": "high",
      "followers": ["Jo", "Will"],
    },
    "status": "active",
    ...
  },
  {
    "title": "Second title",
    "comment": "Second comment.",
    "characteristics": {
      "length": "long",
      "accuracy": "medium"
      "followers": [ ],
    },
    "status": "pending",
    ...
  },
  ...
  ]
}

Anfrage für eine Teilantwort: In der folgenden Anfrage für dieselbe Ressource wird der Parameter fields verwendet, um die zurückgegebene Datenmenge erheblich zu verringern.

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

Teilantwort: Als Antwort auf die obige Anfrage sendet der Server eine Antwort zurück, die zusammen mit einem einfachen Array nur die Art von Informationen enthält, die ausschließlich HTML-Titel und Längeneigenschaften in jedem Element umfassen.

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

Die Antwort ist ein JSON-Objekt, das nur die ausgewählten Felder und deren einschließende übergeordnete Objekte enthält.

Details zur Formatierung des fields-Parameters werden als Nächstes beschrieben, gefolgt von weiteren Informationen zum genauen Inhalt, der in der Antwort zurückgegeben wird.

Parameter „Fields“ – Syntaxzusammenfassung

Das Format des fields-Anfrageparameters basiert grob auf der XPath-Syntax. Die unterstützte Syntax wird unten zusammengefasst. Zusätzliche Beispiele findest du im darauffolgenden Abschnitt.

  • Mit einer durch Kommas getrennten Liste kannst du mehrere Felder auswählen.
  • Verwende a/b, um das Feld b auszuwählen, das im Feld a verschachtelt ist. Verwende a/b/c, um das Feld c auszuwählen, das in b verschachtelt ist.

    Ausnahme: Bei API-Antworten mit „data“-Wrappern, bei denen die Antwort in einem Objekt vom Typ data in der Form data: { ... } verschachtelt ist, darf „data“ nicht in die Spezifikation von fields aufgenommen werden. Die Aufnahme eines Datenobjekts in einer Feldspezifikation wie data/a/b führt zu einem Fehler. Verwende stattdessen eine fields-Angabe wie a/b.

  • Verwende ein untergeordnetes Auswahlzeichen, um eine Reihe von untergeordneten Feldern von Arrays oder Objekten anzufordern. Setze dazu Ausdrücke in Klammern: „( )“.

    Beispiel: fields=items(id,author/email) gibt nur die Element-ID und die E-Mail-Adresse des Autors für jedes Element im Array zurück. Du kannst auch ein einzelnes Teilfeld angeben, in dem fields=items(id) fields=items/id entspricht.

  • Verwende gegebenenfalls Platzhalter bei der Feldauswahl.

    Zum Beispiel: Mit fields=items/pagemap/* werden alle Objekte in einer Pagemap ausgewählt.

Weitere Beispiele zur Verwendung des Parameters „Fields“

In diesen Beispielen wird auch beschrieben, wie sich der Wert des Parameters fields auf die Antwort auswirkt.

Hinweis: Wie bei allen Abfrageparameterwerten muss der Wert des Parameters fields URL-codiert sein. Für eine bessere Lesbarkeit enthalten die Beispiele in diesem Dokument keine Codierung.

Identifiziere die Felder, die zurückgegeben werden sollen, oder triff eine Feldauswahl.
Der Wert des Anfrageparameters fields besteht aus einer durch Kommas getrennten Liste mit Feldern. Jedes Feld muss relativ zum Stamm der Antwort angegeben werden. Wenn du also einen list-Vorgang ausführst, besteht die Antwort aus einer Sammlung und enthält im Allgemeinen ein Ressourcen-Array. Bei einem Vorgang, der eine einzelne Ressource zurückgibt, werden die Felder bezogen auf diese Ressource angegeben. Wenn das ausgewählte Feld ein Array (oder ein Teil eines Arrays) ist, gibt der Server den ausgewählten Teil aller Elemente in dem Array zurück.

Hier siehst du einige Beispiele für die Sammlungsebene:
Beispiele Auswirkung
items Gibt alle Elemente im Element-Array zurück, einschließlich aller Felder in jedem Element, jedoch keine anderen Felder.
etag,items Gibt sowohl das etag-Feld als auch alle Elemente im Element-Array zurück.
items/title Gibt nur das title-Feld für alle Elemente im Element-Array zurück.

Wenn ein verschachteltes Feld zurückgegeben wird, umfasst die Antwort die einschließenden übergeordneten Objekte. Die übergeordneten Felder enthalten nur dann andere untergeordnete Felder, wenn diese ausdrücklich ausgewählt wurden.
context/facets/label Gibt nur das Feld label für alle Mitglieder des Arrays facets zurück, das selbst unter dem Objekt context verschachtelt ist.
items/pagemap/*/title Gibt für jedes Element im Element-Array nur das title-Feld (sofern vorhanden) aller Objekte zurück, die untergeordnete Objekte von pagemap sind.

Hier siehst du einige Beispiele für die Ressourcenebene:
Beispiele Auswirkung
title Gibt das Feld title der angeforderten Ressource zurück.
author/uri Gibt das Unterfeld uri des Objekts author in der angeforderten Ressource zurück.
links/*/href
Gibt das Feld href aller Objekte zurück, die untergeordnete Objekte von links sind.
Mit der untergeordneten Auswahl forderst du nur Teile bestimmter Felder an.
Wenn in deiner Anfrage bestimmte Felder spezifiziert werden, gibt der Server standardmäßig die Objekte oder Array-Elemente in ihrer Gesamtheit zurück. Du kannst eine Antwort angeben, die nur bestimmte untergeordnete Felder enthält. Dazu verwendest du die Syntax „( )“ für die untergeordnete Auswahl wie im folgenden Beispiel dargestellt.
Beispiel Auswirkung
items(title,author/uri) Gibt nur die Werte von title und den uri des Autors für jedes Element im Element-Array zurück.

Umgang mit Teilantworten

Nachdem ein Server eine gültige Anfrage verarbeitet hat, die den fields-Abfrageparameter enthält, sendet er einen 200 OK-HTTP-Statuscode zusammen mit den angeforderten Daten zurück. Wenn der fields-Abfrageparameter einen Fehler enthält oder ungültig ist, gibt der Server den HTTP-Statuscode 400 Bad Request zusammen mit einer Fehlermeldung zurück, die den Nutzer darüber informiert, was bei seiner Feldauswahl falsch war (Beispiel: "Invalid field selection a/b").

Hier ein Beispiel für eine Teilantwort, die im einführenden Abschnitt oben dargestellt wurde. Mit dem Parameter fields gibt die Anfrage an, welche Felder zurückgegeben werden sollen.

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

Die Teilantwort sieht folgendermaßen aus:

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

Hinweis: Bei APIs, die Abfrageparameter für die Datenpaginierung (wie beispielsweise maxResults und nextPageToken) unterstützen, kannst du mit diesen Parametern die Ergebnisse der einzelnen Abfragen auf eine überschaubare Größe reduzieren. Andernfalls wären Leistungssteigerungen mit einer Teilantwort eventuell nicht realisierbar.

Patch (Teilaktualisierung)

Du kannst auch das Senden von unnötigen Daten vermeiden, wenn du Ressourcen änderst. Wenn du das HTTP-Verb PATCH verwendest, sendest du aktualisierte Daten nur für die Felder, die du änderst. Die Patchsemantik, die in diesem Dokument beschrieben wird, ist anders (und einfacher) als die der älteren GData-Implementierung einer Teilaktualisierung.

Das kurze Beispiel unten zeigt, wie mit einer Patchanfrage die Datenmenge minimiert wird, die du zur Durchführung eines kleinen Updates benötigst.

Beispiel

Dieses Beispiel zeigt eine einfache Patchanfrage, mit der nur der Titel einer allgemeinen (fiktiven) „Demo“-API-Ressource aktualisiert werden soll. Die Ressource enthält auch einen Kommentar, eine Reihe von Eigenschafts- und Statusfeldern sowie viele andere Felder. Diese Anfrage sendet jedoch nur das Feld title, da dieses Feld als einziges geändert wird:

PATCH https://www.googleapis.com/demo/v1/324
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "title": "New title"
}

Lösung:

200 OK
{
  "title": "New title",
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "accuracy": "high",
    "followers": ["Jo", "Will"],
  },
  "status": "active",
  ...
}

Der Server gibt einen 200 OK-Statuscode zusammen mit der vollständigen Darstellung der aktualisierten Ressource zurück. Da nur das Feld title in der Patchanfrage enthalten war, ist dies der einzige Wert, der sich von den vorherigen Werten unterscheidet.

Hinweis: Wenn du den Parameter fields einer Teilantwort in Verbindung mit einem Patch verwendest, kannst du Aktualisierungsanfragen noch weiter optimieren. Mit einer Patchanfrage wird nur die Größe der Anfrage reduziert. Bei einer Teilantwort wird die Größe der Antwort reduziert. Verwende also eine Patchanfrage mit dem fields-Parameter, um die in beiden Richtungen gesendete Datenmenge zu reduzieren.

Semantik einer Patchanfrage

Der Text der Patchanfrage umfasst nur die Ressourcenfelder, die du ändern möchtest. Bei der Angabe eines Feldes musst du einschließende übergeordnete Objekte einbeziehen, da die einschließenden übergeordneten Objekte mit einer Teilantwort zurückgegeben werden. Die geänderten Daten, die du sendest, werden mit den Daten für das übergeordnete Objekt zusammengeführt, sofern dieses vorhanden ist.

  • Hinzufügen: Wenn du ein Feld hinzufügen möchtest, das noch nicht vorhanden ist, musst du das neue Feld und dessen Wert angeben.
  • Ändern: Zum Ändern des Werts eines vorhandenen Felds gibst du das Feld an und legst es auf den neuen Wert fest.
  • Löschen: Gib das zu löschende Feld an und setze es auf null. Beispiel: "comment": null. Du kannst auch ein ganzes Objekt löschen (wenn es änderbar ist). Setze es dafür auf null. Bei der Java API-Clientbibliothek verwendest du stattdessen Data.NULL_STRING. Weitere Informationen findest du unter JSON null.

Hinweis zu Arrays: Bei Patchanfragen, die Arrays enthalten, wird das vorhandene Array durch das Array ersetzt, das du angibst. Elemente in einem Array können nicht einzeln geändert, hinzugefügt oder gelöscht werden.

Patch in einem Read-Modify-Write-Zyklus verwenden

Es kann in der Praxis sinnvoll sein, als Erstes eine Teilantwort mit den Daten abzurufen, die du ändern möchtest. Dies ist besonders wichtig bei Ressourcen, die ETags verwenden. Du musst nämlich den aktuellen ETag-Wert im HTTP-Header If-Match angeben, um die Ressource zu aktualisieren. Nachdem du die Daten abgerufen hast, kannst du die gewünschten Werte ändern und die geänderte Teildarstellung mit einer Patchanfrage wieder zurücksenden. Beim folgenden Beispiel wird davon ausgegangen, dass die Demo-Ressource ETags verwendet:

GET https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token

Dies ist die Teilantwort:

200 OK
{
  "etag": "ETagString"
  "title": "New title"
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "level": "5",
    "followers": ["Jo", "Will"],
  }
}

Die folgende Patchanfrage basiert auf dieser Antwort. Wie unten dargestellt, verwendet die Anfrage auch den Parameter fields, um die in der Patchantwort zurückgegebenen Daten zu begrenzen:

PATCH https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json
If-Match: "ETagString"
{
  "etag": "ETagString"
  "title": "",                  /* Clear the value of the title by setting it to the empty string. */
  "comment": null,              /* Delete the comment by replacing its value with null. */
  "characteristics": {
    "length": "short",
    "level": "10",              /* Modify the level value. */
    "followers": ["Jo", "Liz"], /* Replace the followers array to delete Will and add Liz. */
    "accuracy": "high"          /* Add a new characteristic. */
  },
}

Der Server antwortet mit dem HTTP-Statuscode „200 OK“ und der Teildarstellung der aktualisierten Ressource:

200 OK
{
  "etag": "newETagString"
  "title": "",                 /* Title is cleared; deleted comment field is missing. */
  "characteristics": {
    "length": "short",
    "level": "10",             /* Value is updated.*/
    "followers": ["Jo" "Liz"], /* New follower Liz is present; deleted Will is missing. */
    "accuracy": "high"         /* New characteristic is present. */
  }
}

Patchanfrage direkt erstellen

In einigen Fällen müssen Patchanfragen auf den zuvor abgerufenen Daten beruhen. Wenn du zum Beispiel einem Array ein Element hinzufügen und keines der vorhandenen Array-Elemente verlieren möchtest, musst du zuerst die vorhandenen Daten abrufen. Wenn eine API ETags verwendet, musst du den vorherigen ETag-Wert mit der Anfrage senden, damit die Ressource aktualisiert werden kann.

Hinweis: Mit dem HTTP-Header "If-Match: *" können Sie einen Patch erzwingen, wenn ETags verwendet werden.  In diesem Fall musst du den Lesevorgang nicht vor dem Schreibvorgang ausführen.

In anderen Fällen kannst du die Patchanfrage direkt erstellen, ohne zuvor die vorhandenen Daten abzurufen. Du kannst beispielsweise jederzeit eine Patchanfrage einrichten, mit der ein Feld mit einem neuen Wert aktualisiert oder ein neues Feld hinzugefügt wird. Hier ein Beispiel:

PATCH https://www.googleapis.com/demo/v1/324?fields=comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "comment": "A new comment",
  "characteristics": {
    "volume": "loud",
    "accuracy": null
  }
}

Wenn das Kommentarfeld bei dieser Anfrage einen vorhandenen Wert hat, wird dieser durch den neuen Wert überschrieben. Ansonsten wird es auf den neuen Wert festgelegt. Wenn eine Volumeneigenschaft vorhanden war, wird ihr Wert ebenfalls überschrieben; wenn nicht, wird sie erstellt. Das Feld „accuracy“ wird entfernt, sofern es festgelegt wurde.

Umgang mit der Antwort auf ein Patch

Nachdem eine gültige Patchanfrage verarbeitet wurde, gibt die API einen HTTP-Antwortcode 200 OK zusammen mit der vollständigen Darstellung der geänderten Ressource zurück. Wenn ETags von der API verwendet werden, aktualisiert der Server ETag-Werte, sobald eine Patchanfrage erfolgreich verarbeitet wird, wie bei PUT.

Die Patchanfrage gibt die vollständige Ressourcendarstellung zurück, es sei denn, du verwendest den fields-Parameter, um die zurückgegebene Datenmenge zu reduzieren.

Wenn eine Patchanfrage zu einem neuen Ressourcenstatus führt, der syntaktisch oder semantisch ungültig ist, gibt der Server einen HTTP-Statuscode 400 Bad Request oder 422 Unprocessable Entity zurück und der Ressourcenstatus bleibt unverändert. Beispiel: Wenn du versuchst, den Wert für ein Pflichtfeld zu löschen, gibt der Server einen Fehler zurück.

Alternative Schreibweise, wenn das PATCH-HTTP-Verb nicht unterstützt wird

Wenn deine Firewall keine PATCH-HTTP-Anfragen unterstützt, führe eine POST-HTTP-Anfrage aus und setze den Überschreibungsheader auf PATCH (wie unten dargestellt):

POST https://www.googleapis.com/...
X-HTTP-Method-Override: PATCH
...

Unterschied zwischen Patch und Aktualisierung

Wenn du in der Praxis Daten für eine Aktualisierungsanfrage sendest, die das HTTP-Verb PUT verwendet, musst du nur die Felder senden, die erforderlich oder optional sind. Wenn du Werte für Felder sendest, die vom Server festgelegt werden, werden diese ignoriert. Obwohl dieser Ansatz wie eine andere Möglichkeit für eine Teilaktualisierung aussieht, hat er einige Einschränkungen. Bei Aktualisierungen, die das HTTP-Verb PUT verwenden, schlägt die Anfrage fehl, wenn erforderliche Parameter nicht angeben werden. Vorher festgelegte Daten werden gelöscht, wenn keine optionalen Parameter angeben werden.

Aus diesem Grund ist die Verwendung eines Patch wesentlich sicherer. Du gibst nur Daten für die Felder an, die du ändern möchtest. Felder, die du auslässt, werden nicht gelöscht. Sich wiederholende Elemente oder Arrays sind die einzige Ausnahme zu dieser Regel. Werden diese allesamt ausgelassen, bleiben sie unverändert. Wenn du ein Element oder Array angibst, wird die gesamte Gruppe durch die Gruppe ersetzt, die du angegeben hast.

Empfohlene Begrenzung für die Anfragehäufigkeit

Wir begrenzen möglicherweise die Häufigkeit, mit der du die Google Wallet API aufrufen kannst. Wir empfehlen, Anfragen auf maximal 20 pro Sekunde zu begrenzen.

Batchanfragen an Google Wallet

Die Google Wallet API unterstützt die Batchverarbeitung von API-Aufrufen, um die Anzahl der Verbindungen zu verringern, die ein Client herstellen muss. Weitere Informationen zur Batchanfrage und zur Antwortstruktur findest du unter Batchdetails.

Der folgende Beispielcode zeigt Batchanfragen. In den Java- und PHP-Beispielen werden die Google Wallet-Bibliotheken verwendet, um die Erstellung von Klassen und Objekten zu vereinfachen.

Java

/**
 * Batch create Google Wallet objects from an existing class.
 *
 * @param issuerId The issuer ID being used for this request.
 * @param classSuffix Developer-defined unique ID for this pass class.
 * @throws IOException
 */
public void BatchCreateObjects(String issuerId, String classSuffix) throws IOException {
  // Create the batch request client
  BatchRequest batch = service.batch(new HttpCredentialsAdapter(credentials));

  // The callback will be invoked for each request in the batch
  JsonBatchCallback<LoyaltyObject> callback =
      new JsonBatchCallback<LoyaltyObject>() {
        // Invoked if the request was successful
        public void onSuccess(LoyaltyObject response, HttpHeaders responseHeaders) {
          System.out.println("Batch insert response");
          System.out.println(response.toString());
        }

        // Invoked if the request failed
        public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
          System.out.println("Error Message: " + e.getMessage());
        }
      };

  // Example: Generate three new pass objects
  for (int i = 0; i < 3; i++) {
    // Generate a random object suffix
    String objectSuffix = UUID.randomUUID().toString().replaceAll("[^\\w.-]", "_");

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyobject
    LoyaltyObject batchObject =
        new LoyaltyObject()
            .setId(String.format("%s.%s", issuerId, objectSuffix))
            .setClassId(String.format("%s.%s", issuerId, classSuffix))
            .setState("ACTIVE")
            .setHeroImage(
                new Image()
                    .setSourceUri(
                        new ImageUri()
                            .setUri(
                                "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"))
                    .setContentDescription(
                        new LocalizedString()
                            .setDefaultValue(
                                new TranslatedString()
                                    .setLanguage("en-US")
                                    .setValue("Hero image description"))))
            .setTextModulesData(
                Arrays.asList(
                    new TextModuleData()
                        .setHeader("Text module header")
                        .setBody("Text module body")
                        .setId("TEXT_MODULE_ID")))
            .setLinksModuleData(
                new LinksModuleData()
                    .setUris(
                        Arrays.asList(
                            new Uri()
                                .setUri("http://maps.google.com/")
                                .setDescription("Link module URI description")
                                .setId("LINK_MODULE_URI_ID"),
                            new Uri()
                                .setUri("tel:6505555555")
                                .setDescription("Link module tel description")
                                .setId("LINK_MODULE_TEL_ID"))))
            .setImageModulesData(
                Arrays.asList(
                    new ImageModuleData()
                        .setMainImage(
                            new Image()
                                .setSourceUri(
                                    new ImageUri()
                                        .setUri(
                                            "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"))
                                .setContentDescription(
                                    new LocalizedString()
                                        .setDefaultValue(
                                            new TranslatedString()
                                                .setLanguage("en-US")
                                                .setValue("Image module description"))))
                        .setId("IMAGE_MODULE_ID")))
            .setBarcode(new Barcode().setType("QR_CODE").setValue("QR code value"))
            .setLocations(
                Arrays.asList(
                    new LatLongPoint()
                        .setLatitude(37.424015499999996)
                        .setLongitude(-122.09259560000001)))
            .setAccountId("Account ID")
            .setAccountName("Account name")
            .setLoyaltyPoints(
                new LoyaltyPoints()
                    .setLabel("Points")
                    .setBalance(new LoyaltyPointsBalance().setInt(800)));

    service.loyaltyobject().insert(batchObject).queue(batch, callback);
  }

  // Invoke the batch API calls
  batch.execute();
}

PHP

/**
 * Batch create Google Wallet objects from an existing class.
 *
 * @param string $issuerId The issuer ID being used for this request.
 * @param string $classSuffix Developer-defined unique ID for the pass class.
 */
public function batchCreateObjects(string $issuerId, string $classSuffix)
{
  // Update the client to enable batch requests
  $this->client->setUseBatch(true);
  $batch = $this->service->createBatch();

  // Example: Generate three new pass objects
  for ($i = 0; $i < 3; $i++) {
    // Generate a random object suffix
    $objectSuffix = preg_replace('/[^\w.-]/i', '_', uniqid());

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyobject
    $batchObject = new Google_Service_Walletobjects_LoyaltyObject([
      'id' => "{$issuerId}.{$objectSuffix}",
      'classId' => "{$issuerId}.{$classSuffix}",
      'state' => 'ACTIVE',
      'heroImage' => new Google_Service_Walletobjects_Image([
        'sourceUri' => new Google_Service_Walletobjects_ImageUri([
          'uri' => 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg'
        ]),
        'contentDescription' => new Google_Service_Walletobjects_LocalizedString([
          'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
            'language' => 'en-US',
            'value' => 'Hero image description'
          ])
        ])
      ]),
      'textModulesData' => [
        new Google_Service_Walletobjects_TextModuleData([
          'header' => 'Text module header',
          'body' => 'Text module body',
          'id' => 'TEXT_MODULE_ID'
        ])
      ],
      'linksModuleData' => new Google_Service_Walletobjects_LinksModuleData([
        'uris' => [
          new Google_Service_Walletobjects_Uri([
            'uri' => 'http://maps.google.com/',
            'description' => 'Link module URI description',
            'id' => 'LINK_MODULE_URI_ID'
          ]),
          new Google_Service_Walletobjects_Uri([
            'uri' => 'tel:6505555555',
            'description' => 'Link module tel description',
            'id' => 'LINK_MODULE_TEL_ID'
          ])
        ]
      ]),
      'imageModulesData' => [
        new Google_Service_Walletobjects_ImageModuleData([
          'mainImage' => new Google_Service_Walletobjects_Image([
            'sourceUri' => new Google_Service_Walletobjects_ImageUri([
              'uri' => 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg'
            ]),
            'contentDescription' => new Google_Service_Walletobjects_LocalizedString([
              'defaultValue' => new Google_Service_Walletobjects_TranslatedString([
                'language' => 'en-US',
                'value' => 'Image module description'
              ])
            ])
          ]),
          'id' => 'IMAGE_MODULE_ID'
        ])
      ],
      'barcode' => new Google_Service_Walletobjects_Barcode([
        'type' => 'QR_CODE',
        'value' => 'QR code value'
      ]),
      'locations' => [
        new Google_Service_Walletobjects_LatLongPoint([
          'latitude' => 37.424015499999996,
          'longitude' =>  -122.09259560000001
        ])
      ],
      'accountId' => 'Account ID',
      'accountName' => 'Account name',
      'loyaltyPoints' => new Google_Service_Walletobjects_LoyaltyPoints([
        'balance' => new Google_Service_Walletobjects_LoyaltyPointsBalance([
          'int' => 800
        ])
      ])
    ]);

    $batch->add($this->service->loyaltyobject->insert($batchObject));
  }

  // Make the batch request
  $batchResponse = $batch->execute();

  print "Batch insert response\n";
  foreach ($batchResponse as $key => $value) {
    if ($value instanceof Google_Service_Exception) {
      print_r($value->getErrors());
      continue;
    }
    print "{$value->getId()}\n";
  }
}

Python

def batch_create_objects(self, issuer_id: str, class_suffix: str):
    """Batch create Google Wallet objects from an existing class.

    The request body will be a multiline string. See below for information.

    https://cloud.google.com/compute/docs/api/how-tos/batch#example

    Args:
        issuer_id (str): The issuer ID being used for this request.
        class_suffix (str): Developer-defined unique ID for this pass class.
    """
    data = ''

    # Example: Generate three new pass objects
    for _ in range(3):
        # Generate a random object suffix
        object_suffix = str(uuid.uuid4()).replace('[^\\w.-]', '_')

        # See link below for more information on required properties
        # https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyobject
        batch_object = {
            'id': f'{issuer_id}.{object_suffix}',
            'classId': f'{issuer_id}.{class_suffix}',
            'state': 'ACTIVE',
            'heroImage': {
                'sourceUri': {
                    'uri':
                        'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg'
                },
                'contentDescription': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': 'Hero image description'
                    }
                }
            },
            'textModulesData': [{
                'header': 'Text module header',
                'body': 'Text module body',
                'id': 'TEXT_MODULE_ID'
            }],
            'linksModuleData': {
                'uris': [{
                    'uri': 'http://maps.google.com/',
                    'description': 'Link module URI description',
                    'id': 'LINK_MODULE_URI_ID'
                }, {
                    'uri': 'tel:6505555555',
                    'description': 'Link module tel description',
                    'id': 'LINK_MODULE_TEL_ID'
                }]
            },
            'imageModulesData': [{
                'mainImage': {
                    'sourceUri': {
                        'uri':
                            'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg'
                    },
                    'contentDescription': {
                        'defaultValue': {
                            'language': 'en-US',
                            'value': 'Image module description'
                        }
                    }
                },
                'id': 'IMAGE_MODULE_ID'
            }],
            'barcode': {
                'type': 'QR_CODE',
                'value': 'QR code'
            },
            'locations': [{
                'latitude': 37.424015499999996,
                'longitude': -122.09259560000001
            }],
            'accountId': 'Account id',
            'accountName': 'Account name',
            'loyaltyPoints': {
                'label': 'Points',
                'balance': {
                    'int': 800
                }
            }
        }

        data += '--batch_createobjectbatch\n'
        data += 'Content-Type: application/json\n\n'
        data += 'POST /walletobjects/v1/loyaltyObject/\n\n'

        data += json.dumps(batch_object) + '\n\n'

    data += '--batch_createobjectbatch--'

    # Invoke the batch API calls
    response = self.http_client.post(
        url='https://walletobjects.googleapis.com/batch',
        data=data,
        headers={
            # `boundary` is the delimiter between API calls in the batch request
            'Content-Type':
                'multipart/mixed; boundary=batch_createobjectbatch'
        })

    print('Batch insert response')
    print(response.content.decode('UTF-8'))

C#

/// <summary>
/// Batch create Google Wallet objects from an existing class.
/// </summary>
/// <param name="issuerId">The issuer ID being used for this request.</param>
/// <param name="classSuffix">Developer-defined unique ID for this pass class.</param>
public async void BatchCreateObjects(string issuerId, string classSuffix)
{
  // The request body will be a multiline string
  // See below for more information
  // https://cloud.google.com/compute/docs/api/how-tos/batch//example
  string data = "";

  HttpClient httpClient = new HttpClient();
  httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer",
    credentials.GetAccessTokenForRequestAsync().Result
  );

  // Example: Generate three new pass objects
  for (int i = 0; i < 3; i++)
  {
    // Generate a random object suffix
    string objectSuffix = Regex.Replace(Guid.NewGuid().ToString(), "[^\\w.-]", "_");

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyobject
    LoyaltyObject batchObject = new LoyaltyObject
    {
      Id = $"{issuerId}.{objectSuffix}",
      ClassId = $"{issuerId}.{classSuffix}",
      State = "ACTIVE",
      HeroImage = new Image
      {
        SourceUri = new ImageUri
        {
          Uri = "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"
        },
        ContentDescription = new LocalizedString
        {
          DefaultValue = new TranslatedString
          {
            Language = "en-US",
            Value = "Hero image description"
          }
        }
      },
      TextModulesData = new List<TextModuleData>
      {
        new TextModuleData
        {
          Header = "Text module header",
          Body = "Text module body",
          Id = "TEXT_MODULE_ID"
        }
      },
      LinksModuleData = new LinksModuleData
      {
        Uris = new List<Google.Apis.Walletobjects.v1.Data.Uri>
        {
          new Google.Apis.Walletobjects.v1.Data.Uri
          {
            UriValue = "http://maps.google.com/",
            Description = "Link module URI description",
            Id = "LINK_MODULE_URI_ID"
          },
          new Google.Apis.Walletobjects.v1.Data.Uri
          {
            UriValue = "tel:6505555555",
            Description = "Link module tel description",
            Id = "LINK_MODULE_TEL_ID"
          }
        }
      },
      ImageModulesData = new List<ImageModuleData>
      {
        new ImageModuleData
        {
          MainImage = new Image
          {
            SourceUri = new ImageUri
            {
              Uri = "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"
            },
            ContentDescription = new LocalizedString
            {
              DefaultValue = new TranslatedString
              {
                Language = "en-US",
                Value = "Image module description"
              }
            }
          },
          Id = "IMAGE_MODULE_ID"
        }
      },
      Barcode = new Barcode
      {
        Type = "QR_CODE",
        Value = "QR code"
      },
      Locations = new List<LatLongPoint>
      {
        new LatLongPoint
        {
          Latitude = 37.424015499999996,
          Longitude = -122.09259560000001
        }
      },
      AccountId = "Account id",
      AccountName = "Account name",
      LoyaltyPoints = new LoyaltyPoints
      {
        Label = "Points",
        Balance = new LoyaltyPointsBalance
        {
          Int__ = 800
        }
      }
    };

    data += "--batch_createobjectbatch\n";
    data += "Content-Type: application/json\n\n";
    data += "POST /walletobjects/v1/loyaltyObject/\n\n";

    data += JsonConvert.SerializeObject(batchObject) + "\n\n";
  }
  data += "--batch_createobjectbatch--";

  // Invoke the batch API calls
  HttpRequestMessage batchObjectRequest = new HttpRequestMessage(
      HttpMethod.Post,
      "https://walletobjects.googleapis.com/batch");

  batchObjectRequest.Content = new StringContent(data);
  batchObjectRequest.Content.Headers.ContentType = new MediaTypeHeaderValue(
      "multipart/mixed");
  // `boundary` is the delimiter between API calls in the batch request
  batchObjectRequest.Content.Headers.ContentType.Parameters.Add(
      new NameValueHeaderValue("boundary", "batch_createobjectbatch"));

  HttpResponseMessage batchObjectResponse = httpClient.Send(
      batchObjectRequest);

  string batchObjectContent = await batchObjectResponse
      .Content
      .ReadAsStringAsync();

  Console.WriteLine("Batch insert response");
  Console.WriteLine(batchObjectContent);
}

Node.js

/**
 * Batch create Google Wallet objects from an existing class.
 *
 * @param {string} issuerId The issuer ID being used for this request.
 * @param {string} classSuffix Developer-defined unique ID for this pass class.
 */
async batchCreateObjects(issuerId, classSuffix) {
  // See below for more information
  // https://cloud.google.com/compute/docs/api/how-tos/batch#example
  let data = '';
  let batchObject;
  let objectSuffix;

  // Example: Generate three new pass objects
  for (let i = 0; i < 3; i++) {
    // Generate a random object suffix
    objectSuffix = uuidv4().replace('[^\w.-]', '_');

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/loyalty-cards/rest/v1/loyaltyobject
    batchObject = {
      'id': `${issuerId}.${objectSuffix}`,
      'classId': `${issuerId}.${classSuffix}`,
      'state': 'ACTIVE',
      'heroImage': {
        'sourceUri': {
          'uri': 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg'
        },
        'contentDescription': {
          'defaultValue': {
            'language': 'en-US',
            'value': 'Hero image description'
          }
        }
      },
      'textModulesData': [
        {
          'header': 'Text module header',
          'body': 'Text module body',
          'id': 'TEXT_MODULE_ID'
        }
      ],
      'linksModuleData': {
        'uris': [
          {
            'uri': 'http://maps.google.com/',
            'description': 'Link module URI description',
            'id': 'LINK_MODULE_URI_ID'
          },
          {
            'uri': 'tel:6505555555',
            'description': 'Link module tel description',
            'id': 'LINK_MODULE_TEL_ID'
          }
        ]
      },
      'imageModulesData': [
        {
          'mainImage': {
            'sourceUri': {
              'uri': 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg'
            },
            'contentDescription': {
              'defaultValue': {
                'language': 'en-US',
                'value': 'Image module description'
              }
            }
          },
          'id': 'IMAGE_MODULE_ID'
        }
      ],
      'barcode': {
        'type': 'QR_CODE',
        'value': 'QR code'
      },
      'locations': [
        {
          'latitude': 37.424015499999996,
          'longitude': -122.09259560000001
        }
      ],
      'accountId': 'Account id',
      'accountName': 'Account name',
      'loyaltyPoints': {
        'label': 'Points',
        'balance': {
          'int': 800
        }
      }
    };

    data += '--batch_createobjectbatch\n';
    data += 'Content-Type: application/json\n\n';
    data += 'POST /walletobjects/v1/loyaltyObject\n\n';

    data += JSON.stringify(batchObject) + '\n\n';
  }
  data += '--batch_createobjectbatch--';

  // Invoke the batch API calls
  let response = await this.httpClient.request({
    url: this.batchUrl,
    method: 'POST',
    data: data,
    headers: {
      // `boundary` is the delimiter between API calls in the batch request
      'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
    }
  });

  console.log('Batch insert response');
  console.log(response);
}