You can use the methods in the
[Watches](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms.watches) collection to
receive notifications when data changes in forms. This page provides a
conceptual overview and instructions for setting up and receiving push
notifications.

## Overview

The Google Forms API push notifications feature allows applications to subscribe
to notifications when data changes in forms. Notifications are delivered to
a [Cloud Pub/Sub](https://cloud.google.com/pubsub) topic, usually within
minutes of the change.

To receive push notifications, you need to set up a Cloud Pub/Sub topic and
provide that topic's name when you create a watch for the appropriate event
type.

Below are definitions of key concepts used in this documentation:

- A *target* is a place where notifications are sent. The only supported target is a Cloud Pub/Sub topic.
- An *event type* is a category of notifications that a third-party application can subscribe to.
- A *watch* is an instruction to the Forms API to deliver notifications for a particular *event type* on a particular form to a *target*.

Once you create a watch for an event type on a particular form, that watch's
target (which is a Cloud Pub/Sub topic) receives notifications from those
events on that form until the watch expires. Your watch lasts a week, but you
can extend it at any point before it expires by making a request to
[watches.renew()](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms.watches/renew).

Your Cloud Pub/Sub topic only receives notifications about forms that you can
view with the credentials you supply. For example, if the user revokes
permission from your application or loses edit access to a watched form,
notifications are no longer delivered.

### Available event types

The Google Forms API currently offers two categories of events:

- `EventType.SCHEMA`, which notifies about edits to a form's content and settings.
- `EventType.RESPONSES`, which notifies when form responses (both new and updated) are submitted.

### Notification responses

Notifications are encoded with JSON and contain:

- The ID of the triggering form
- The ID of the triggering watch
- The type of event that triggered the notification
- Other fields set by Cloud Pub/Sub, such as `messageId` and `publishTime`

Notifications **do not** contain detailed form or response data. After each
notification is received, a separate API call is required to fetch fresh data.
See [Suggested usage](https://developers.google.com/workspace/forms/api/guides/push-notifications#suggested_usage) for how to accomplish this.

The following snippet demonstrates a sample notification for a schema change:

    {
      "attributes": {
        "eventType": "SCHEMA",
        "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
        "watchId": "892515d1-a902-444f-a2fe-42b718fe8159"
      },
      "messageId": "767437830649",
      "publishTime": "2021-03-31T01:34:08.053Z"
    }

The following snippet demonstrates a sample notification for a new response:

    {
      "attributes": {
        "eventType": "RESPONSES",
        "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
        "watchId": "5d7e5690-b1ff-41ce-8afb-b469912efd7d"
      },
      "messageId": "767467004397",
      "publishTime": "2021-03-31T01:43:57.285Z"
    }

## Set up a Cloud Pub/Sub topic

Notifications are delivered to Cloud Pub/Sub topics. From Cloud Pub/Sub, you
can receive notifications on a web hook, or by polling a subscription endpoint.

To set up a Cloud Pub/Sub topic, do the following:

1. Complete the [Cloud Pub/Sub Prerequisites](https://cloud.google.com/pubsub/prereqs).
2. [Set up a Cloud Pub/Sub client](https://cloud.google.com/pubsub/configure).
3. Review the [Cloud Pub/Sub pricing](https://cloud.google.com/pubsub/pricing), and enable billing for your Google Cloud console project.
4. Create a Cloud Pub/Sub topic one of three ways:

   - using the [Google Cloud console](https://cloud.google.com/pubsub/docs/quickstart-console#create_a_topic)
   - using the [command line tool](https://cloud.google.com/pubsub/docs/quickstart-cli#use_the_gcloud_command-line_tool) (for simple programmatic use) or
   - using the [Cloud Pub/Sub API](https://cloud.google.com/pubsub/docs/quickstart-client-libraries#create_a_topic_and_a_subscription).

   > [!NOTE]
   > **Note:** Cloud Pub/Sub only allows a [limited number of topics](https://cloud.google.com/pubsub/quotas), so using one topic to receive all your notifications ensures that you don't hit scaling issues if your application becomes popular.

5. [Create a Subscription in Cloud Pub/Sub](https://cloud.google.com/pubsub/docs/subscriber)
   to tell Cloud Pub/Sub how to deliver your notifications.

6. Finally, before creating watches that target your topic, you need to
   [grant permission](https://cloud.google.com/pubsub/docs/access_control#console)
   to the Forms notifications service account
   (forms-notifications@system.gserviceaccount.com) to publish to your topic.

## Create a watch

Once you have a topic that the Forms API push notifications service
account can publish to, you can create notifications using the
[watches.create()](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms.watches/create)
method. This method validates that the provided Cloud Pub/Sub topic can be
reached by the push notifications service account, and fails if it cannot
reach the topic; for example, if the topic does not exist or you have not
granted it publish permission on that topic.

### Python

forms/snippets/create_watch.py [View on GitHub](https://github.com/googleworkspace/python-samples/blob/main/forms/snippets/create_watch.py)

```python
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)

service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

watch = {
    "watch": {
        "target": {"topic": {"topicName": "<YOUR_TOPIC_PATH>"}},
        "eventType": "RESPONSES",
    }
}

form_id = "<YOUR_FORM_ID>"

# Print JSON response after form watch creation
result = service.forms().watches().create(formId=form_id, body=watch).execute()
print(result)
```

### Node.js

forms/snippets/create_watch.js [View on GitHub](https://github.com/googleworkspace/node-samples/blob/main/forms/snippets/create_watch.js)

```javascript
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Creates a watch on a form to get notifications for new responses.
 */
async function createWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // The request body to create a watch.
  const watchRequest = {
    watch: {
      target: {
        topic: {
          // TODO: Replace with a valid Cloud Pub/Sub topic name.
          topicName: 'projects/<YOUR_TOPIC_PATH>',
        },
      },
      // The event type to watch for. 'RESPONSES' is the only supported type.
      eventType: 'RESPONSES',
    },
  };

  // Send the request to create the watch.
  const result = await formsClient.forms.watches.create({
    formId: formID,
    requestBody: watchRequest,
  });

  console.log(result.data);
  return result.data;
}
```

## Delete a watch

### Python

forms/snippets/delete_watch.py [View on GitHub](https://github.com/googleworkspace/python-samples/blob/main/forms/snippets/delete_watch.py)

```python
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after deleting a form watch
result = (
    service.forms().watches().delete(formId=form_id, watchId=watch_id).execute()
)
print(result)
```

### Node.js

forms/snippets/delete_watch.js [View on GitHub](https://github.com/googleworkspace/node-samples/blob/main/forms/snippets/delete_watch.js)

```javascript
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Deletes a watch from a form.
 */
async function deleteWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to delete the watch.
  const result = await formsClient.forms.watches.delete({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}
```

### Authorization

Like all calls to the Forms API, calls to `watches.create()` must
be authorized with an authorization token. The token must include a scope
that grants read access to the data about which notifications are being
sent.

- For schema changes, this means any scope that grants read access to [forms](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms) using [forms.get()](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms/get).
- For responses, this means any scope that grants read access to [form responses](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms.responses), for example using [forms.responses.list()](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms.responses/list).

For notifications to be delivered, the application must retain an OAuth grant
from the authorized user with the required scopes. If the user disconnects the
application, notifications cease and the watch may be suspended with an error.
To resume notifications after regaining authorization, see
[Renew a watch](https://developers.google.com/workspace/forms/api/guides/push-notifications#renew-watch).

## List a form's watches

### Python

forms/snippets/list_watches.py [View on GitHub](https://github.com/googleworkspace/python-samples/blob/main/forms/snippets/list_watches.py)

```python
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"

# Print JSON list of form watches
result = service.forms().watches().list(formId=form_id).execute()
print(result)
```

### Node.js

forms/snippets/list_watches.js [View on GitHub](https://github.com/googleworkspace/node-samples/blob/main/forms/snippets/list_watches.js)

```javascript
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Lists the watches for a given form.
 */
async function listWatches() {
  // Authenticate with Google and get an authorized client.
  const auth = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/forms.responses.readonly',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth,
  });

  // Get the list of watches for the form.
  const result = await formsClient.forms.watches.list({
    formId: formID,
  });

  console.log(result.data);
  return result.data;
}
```

## Renew a watch

### Python

forms/snippets/renew_watch.py [View on GitHub](https://github.com/googleworkspace/python-samples/blob/main/forms/snippets/renew_watch.py)

```python
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after renewing a form watch
result = (
    service.forms().watches().renew(formId=form_id, watchId=watch_id).execute()
)
print(result)
```

### Node.js

forms/snippets/renew_watch.js [View on GitHub](https://github.com/googleworkspace/node-samples/blob/main/forms/snippets/renew_watch.js)

```javascript
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Renews a watch on a form.
 */
async function renewWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to renew the watch.
  const result = await formsClient.forms.watches.renew({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}
```

## Throttling

Notifications are throttled---each watch can receive at most one notification
every thirty seconds. This threshold of frequency is subject to change.

Because of throttling, a single notification may correspond to multiple events.
In other words, a notification indicates that one or more events have occurred
since the last notification.

## Limits

At any time, for a given form and event type, each Cloud Console project
can have:

- up to 20 watches total
- up to one watch per end user

Additionally, at any time, each form is limited to 50 watches per event type in
total across all Cloud Console projects.

A watch is associated with an end user when it is created or renewed with
credentials for that user. A watch is suspended if the associated end user
loses access to the form or revokes the app's access to the form.

## Reliability

Each watch is notified at least once after each event in all but
extraordinary circumstances. In the vast majority of cases, a notification
is delivered within minutes of an event.

## Errors

If notifications for a watch persistently fail to be delivered, the watch state
becomes `SUSPENDED` and the watch's `errorType` field is set. To reset a
suspended watch's state to `ACTIVE` and resume notifications, see
[Renew a watch](https://developers.google.com/workspace/forms/api/guides/push-notifications#renew-watch).

## Suggested usage

- Use a single Cloud Pub/Sub topic as the target of many watches.
- When receiving a notification on a topic, the form ID is included in the notification payload. Use it with the event type to know what data to fetch and which form to fetch it from.
- To fetch updated data after a notification with `EventType.RESPONSES`, call [forms.responses.list()](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms.responses/list).
  - Set the filter on the request to `timestamp > timestamp_of_the_last_response_you_fetched`.
- To fetch updated data after a notification with `EventType.SCHEMA`, call [forms.get()](https://developers.google.com/workspace/forms/api/reference/rest/v1/forms/get).