Skip to navigation

Authentication

Authenticate with the Zuora API using OAuth 2.0 client credentials
View as Markdown

Zuora uses OAuth 2.0 client credentials for API authentication. You exchange a client ID and secret for a time-limited Bearer token, then include that token in every API request.

Legacy API keys (apiAccessKeyId / apiSecretAccessKey) are deprecated. Migrate to OAuth 2.0 for all new integrations.

Create an OAuth Client

1

Open Zuora Settings

Log in to the Zuora UI and navigate to Settings > Administration > Manage Users.

2

Create a new OAuth client

Click Create OAuth Client. Enter a descriptive name (e.g., “Billing Integration”) and select the appropriate user whose permissions the client should inherit.

3

Save your credentials

Copy the Client ID and Client Secret immediately. The secret is only displayed once.

Store your client secret securely. Never commit it to version control or expose it in client-side code.

Get a Bearer Token

Call the /oauth/token endpoint with your credentials:

curl -X POST https://rest.zuora.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=b64e42ba-7e1a-4bc6-9146-5e1b420306b5" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "grant_type=client_credentials"

Token Response

{
"access_token": "c652cbc0ea384b9f81856a93a2a74538",
"token_type": "bearer",
"expires_in": 3599,
"scope": "user.7c4d5433dc234c369a01b9719ecd059f entity.1a2b7a37-3e7d-4cb3-b0e2-883de9e766cc tenant.19",
"jti": "c652cbc0ea384b9f81856a93a2a74539"
}
FieldDescription
access_tokenThe Bearer token to use in API requests
expires_inToken lifetime in seconds (typically 3599 = ~1 hour)
scopePermissions granted, including user, entity, and tenant
token_typeAlways bearer

Use the Token

Include the token in the Authorization header of every API request:

curl -X GET https://rest.zuora.com/v1/accounts \
-H "Authorization: Bearer c652cbc0ea384b9f81856a93a2a74538" \
-H "Content-Type: application/json"

Token Management Best Practices

Tokens expire after approximately 1 hour. Implement token caching and automatic refresh in your integration.

import time
import requests
class ZuoraClient:
def __init__(self, client_id: str, client_secret: str, base_url: str):
self.client_id = client_id
self.client_secret = client_secret
self.base_url = base_url
self._token = None
self._token_expiry = 0
def _get_token(self) -> str:
if self._token and time.time() < self._token_expiry - 60:
return self._token
response = requests.post(
f"{self.base_url}/oauth/token",
data={
"client_id": self.client_id,
"client_secret": self.client_secret,
"grant_type": "client_credentials",
},
)
response.raise_for_status()
data = response.json()
self._token = data["access_token"]
self._token_expiry = time.time() + data["expires_in"]
return self._token
def request(self, method: str, path: str, **kwargs) -> requests.Response:
headers = kwargs.pop("headers", {})
headers["Authorization"] = f"Bearer {self._get_token()}"
headers["Content-Type"] = "application/json"
return requests.request(
method, f"{self.base_url}{path}", headers=headers, **kwargs
)

Key recommendations

  1. Cache tokens — Reuse a token until it expires instead of requesting a new one per API call.
  2. Refresh proactively — Refresh the token 60 seconds before expiry to avoid failed requests.
  3. Handle 401 errors — If you receive a 401 Unauthorized, refresh the token and retry.
  4. Avoid rate limits — The /oauth/token endpoint has its own rate limit. Requesting too many tokens triggers a 429.
  5. Scope awareness — The token scope determines which entities and operations are accessible.

Multi-Entity Authentication

If your Zuora tenant uses Multi-entity, include the Zuora-Entity-Ids header to target a specific entity:

curl -X GET https://rest.zuora.com/v1/accounts \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Zuora-Entity-Ids: 11e643f4-a3ee-8bad-b061-0025904c756d" \
-H "Content-Type: application/json"

The entity ID must be a valid UUID. You can find entity IDs in Settings > Administration > Manage Entities.