We take security and the integrity of your data seriously.

All access to our APIs uses HTTPS (HTTP over TLS), and before you can make any request to the REST API you need to authenticate your client.

What you need

Service Account credentials

You need to have created a Service Account with access to a project and note noted down its e-mail, the key ID and its secret.

See: Service Accounts

The authentication flow

All our REST APIs are secured via an OAuth2 authentication flow, using Service Accounts for access control and JWT as the medium for the exchange.

Authenticating a client is a 3 step process: 

1. Create JWT 2. Exchange for Access Token 3. Access API with Access Token


1. Create JWT

A JWT, or Java Web Token, is a JSON which contains three fields:

  1. A short header describing the token itself
  2. A payload containing the authentication claim
  3. A signature calculated of the entire JWT + a private secret

These fields are then encoded (using Base64-URL), before being sent, into a compact format dot (".") separated format (111.2222.33333).

This guide will not cover all the details of JWT.

For a great introduction of what JWT is, an interactive JWT editor and an exhaustive list of client libraries, please see

Filling in the JWT with Service Account credentials


  "alg": "HS256", // The signing algorithm used
  "kid": "<Your Service Account Key ID>"

Replace "<Your Service Account Key ID>" with the Key ID from your Service Account:



  "iat": 1525392000, // Current (Unix) time
  "exp": 1525395600, // Expiration (Unix) time
  "aud": "", // Intended recipient
  "iss": "<Your Service Account email>"

Valid iat and exp

  1. The iat and exp are in a standard Unix timestamp format (seconds since January 1, 1970) and these only apply the sent JWT. That is, these have nothing to do with the expiration time of the Access Token.
  2. The maximum duration between iat and exp is 1 hour.

Please also note that some standard libraries (like JavaScripts getTime()) do not return seconds by default.

Replace "<Your Service Account email>" with the e-mail of your Service Account from Studio:


Sign and encode

The signature is calculated over the head, payload and a secret, typically by the JWT client library.

This is a (Python) code-snippet that shows what the encoding and signing step might look like:

# ...
encoded_jwt = jwt.encode(payload=jwt_payload,
# ...

The "key_secret" here would be the Service Account secret created when its Key was created:


Once you have signed and encoded the JWT, it can be exchanged the Access Token needed to talk to the API.


2. Exchange for Access Token

The encoded JWT, from the previous section, is exchanged for an Access Token by sending a POST request to the following endpoint:

The post request has to contain the following two parameters:

  1. "assertion" - containing the encoded JWT string
  2. "grant_type" - containing the string "urn:ietf:params:oauth:grant-type:jwt-bearer"

Form encoding

The POST request requires both parameters to be sent using form encoding. That is, using the following content-type: "application/x-www-form-urlencoded".

The "grant_type" string specifies that you want to exchange a JWT for an Access Token.

For testing purposes you can use curl command to shows how a POST such as this one could look like:

curl -X "POST" "" \
-H 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' \
--data-urlencode "assertion=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJiN2tuc2oyNHRlMDAwYjI0dGpnIn0.eyJpYXQiOjE1MjM1MzQ3MTgsImV4cCI6MTUyMzUzODMxOCwiYXVkIjoiaHR0cHM6Ly9pZGVudGl0eS5kaXNydXB0aXZlLXRlY2hub2xvZ2llcy5jb20vb2F1dGgyL3Rva2VuIiwiaXNzIjoiYmFiNmF0MzI0dGUwMDBiMjR0ZmdAYmFhMDl0YmJlOTNnMDBhcXJzOGcuc2VydmljZWFjY291bnQuZDIxcy5jb20ifQ.vcDKU2Gv4uo_xzV5a3LnQrKTtW2vFop-nidBJA_pfHE" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer"

You would normally do this exchange programmatically. The following example shows an implementation in Axios.

"method": "POST",
"url": "",
"headers": {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
"data": "assertion=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJiN2tuc2oyNHRlMDAwYjI0dGpnIn0.eyJpYXQiOjE1MjM1MzQ3MTgsImV4cCI6MTUyMzUzODMxOCwiYXVkIjoiaHR0cHM6Ly9pZGVudGl0eS5kaXNydXB0aXZlLXRlY2hub2xvZ2llcy5jb20vb2F1dGgyL3Rva2VuIiwiaXNzIjoiYmFiNmF0MzI0dGUwMDBiMjR0ZmdAYmFhMDl0YmJlOTNnMDBhcXJzOGcuc2VydmljZWFjY291bnQuZDIxcy5jb20ifQ.vcDKU2Gv4uo_xzV5a3LnQrKTtW2vFop-nidBJA_pfHE&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer"

The response body will be a JSON containing the Access Token ("access_token") and its expiration time which will be 3600 seconds, 1 hour.

  "access_token": "0123456789abcdef",
  "token_type": "bearer",
  "expires_in": 3600

Common errors

  • If the assertion (encoded JWT) is missing or malformed
  • If the service account or service account key is missing
"error": "invalid_grant"
  • If the grant_type is missing or malformed.
  • If the content-type is not application/x-www-form-urlencoded
"error": "unsupported_grant_type"

"Refreshing" the Access Token

The Access Token you receive is only valid for 1 hour, after which calling the API with this Access Token will return a "401 - Unauthorized" error code.

To get a new Access Token, or "refresh" it, you will have to create a new JWT by following step 2 above. This JWT can contain the exact same content as the previous one, but you might have to update the iat and exp based on the new current time.

For uninterrupted API access, it is possible to retrieve a new Access Token before the old one expires.


3. Access API with Access Token

Once you have the Access Token, you need to include this with every call to the API.

This is done by inserting the "access_token" string into a HTTP header named "Authorization", with the "Bearer "-prefix:

Authorization: Bearer <access_token>

For example:

Authorization: Bearer b2353e6360064d628b611c76592a25f0

A complete example on how to list Projects (that your Service Account has access to), using curl:

curl -X GET "" -H "Authorization: Bearer b2353e6360064d628b611c76592a25f0"

Good job!

You now have all the knowledge you need to authenticate and start using our secure API.