Skip to main content
Insyrge - Zoho CRM & Business Automation Consulting
← Back to All ArticlesZoho CRM & Automation

Zoho OAuth Tokens: Expiry, Limits and Refresh

How Zoho access and refresh tokens work, the limits that break multi-app setups, data centre domains, and a central token service that avoids them.

•Manav Sharma
Zoho OAuth Tokens: Expiry, Limits and Refresh

Zoho access tokens last one hour; refresh tokens don't expire but only 20 are kept per user. Learn the limits and manage tokens for many apps safely.

TL;DR: Zoho CRM uses OAuth 2.0. Per Zoho's documentation, access tokens last one hour, refresh tokens last until revoked, and only 20 refresh tokens are kept per user, so a 21st silently invalidates the oldest. Generating a token per API call triggers 'Access Denied'. Use one client per integration and a central service that caches tokens.

Key takeaways

  • Access token: 1 hour. Refresh token: until revoked.
  • Max 20 refresh tokens per user; the 21st kills the oldest.
  • Max 10 access tokens per refresh token per 10 minutes.
  • Request tokens from your data centre's accounts server.
  • One client per integration, central token cache, secrets manager.

As a Zoho setup grows, so does the number of things that talk to it: a website form handler, an ERP connector, a reporting job, a mobile app, a few scripts someone wrote last year. Each one needs credentials, and each one usually handles them differently. Before long there are refresh tokens in config files, access tokens being generated on every request, and an integration that stops working because another one created too many tokens. Good Zoho OAuth token management fixes this with one clear design: least-privilege clients, a central place that refreshes and caches tokens, secure storage and a plan for rotation and revocation. This guide explains how Zoho's tokens behave, the mistakes we see most, and a worked example of a token service.

How Zoho OAuth tokens work

Zoho CRM uses OAuth 2.0. The OAuth overview and token validity pages describe the details. The essentials:

TokenLifetime and limits (per Zoho's documentation)How to treat itGrant token (authorisation code)One-time use; valid for three minutes by defaultExchange immediately for access and refresh tokensAccess tokenValid for one hour; up to 15 active per refresh token; up to 10 can be generated from a refresh token in 10 minutesCache and reuse until shortly before expiryRefresh tokenDoes not expire until revoked; up to 20 stored per user, with the oldest invalidated when a 21st is createdStore as a secret; never log or share it

Two consequences matter in practice. Generating a new access token for every API call will hit the throttle quickly and cause "Access Denied" errors. And creating refresh tokens repeatedly for the same user can silently invalidate the oldest one, breaking whichever integration was using it.

How long does a Zoho OAuth access token last?

One hour, per Zoho's token validity documentation. Cache it and reuse it until shortly before it expires.

Do Zoho refresh tokens expire?

No, they last until revoked. But Zoho stores up to 20 refresh tokens per user, and creating a 21st invalidates the oldest, which can break another integration.

Why do I get 'Access Denied' when refreshing Zoho tokens?

Usually because too many access tokens are generated: Zoho allows up to 10 from a refresh token in 10 minutes. Caching tokens centrally avoids it.

Which accounts URL should I use for my Zoho data centre?

Zoho runs separate data centres, and tokens must be requested from the accounts server for the user's region, such as accounts.zoho.com, accounts.zoho.eu or accounts.zoho.in. The token response includes an api_domain value that tells you which API domain to call. Zoho's multi-DC documentation explains the setup. Tokens are also specific to an organisation: a refresh token generated for production cannot be used for a sandbox.

Common token management mistakes

  • One "admin" refresh token with full scopes shared by every integration
  • Refresh tokens stored in source code, spreadsheets or chat messages
  • Access tokens generated on every request instead of cached
  • Several services refreshing the same token independently and racing each other
  • Hard-coded API domains that break for users in another data centre
  • No record of which client, user and scopes each integration uses
  • Integrations tied to an employee's account, which break when that person leaves

Related reading: Zoho CRM API limits.

How do you manage Zoho tokens across many apps?

One client per integration, with minimum scopes

Register a separate client for each integration in the Zoho API console and request only the scopes it needs, for example read-only access to Deals rather than full access to all modules. If one integration is compromised, the damage is contained, and revoking it does not affect the others.

A dedicated integration user

Authorise integrations with a dedicated user whose role and profile limit what it can see and change. Audit logs then show clearly which changes came from integrations.

A central token service

One component, not every script, holds refresh tokens, refreshes access tokens shortly before they expire, caches them and hands them to authorised callers. This respects Zoho's throttles and removes race conditions.

Secure storage

Keep refresh tokens and client secrets in a secrets manager or encrypted store with restricted access and audit logging. The OWASP Secrets Management Cheat Sheet covers good practice.

Connections for Deluge

Inside Zoho, Deluge Connections manage OAuth for you when functions call other Zoho services or external APIs. Use them instead of pasting tokens into scripts.

Related reading: Zoho One consulting.

Worked example: a token service in Python

The illustrative class below caches one access token per integration, refreshes it five minutes before expiry, and uses a lock so concurrent callers never trigger parallel refreshes.

import threading, time, requests

class ZohoTokenService:
    def __init__(self, accounts_url, client_id, client_secret, refresh_token):
        self.accounts_url = accounts_url          # e.g. https://accounts.zoho.in
        self.client_id = client_id
        self.client_secret = client_secret
        self.refresh_token = refresh_token        # loaded from a secrets manager
        self._token, self._expires_at, self.api_domain = None, 0, None
        self._lock = threading.Lock()

    def get(self):
        with self._lock:
            if self._token and time.time() < self._expires_at - 300:
                return self._token, self.api_domain
            resp = requests.post(f"{self.accounts_url}/oauth/v2/token", data={
                "refresh_token": self.refresh_token,
                "client_id": self.client_id,
                "client_secret": self.client_secret,
                "grant_type": "refresh_token",
            }, timeout=30)
            body = resp.json()
            if "access_token" not in body:
                raise RuntimeError(f"Token refresh failed: {body.get('error')}")
            self._token = body["access_token"]
            self._expires_at = time.time() + int(body.get("expires_in", 3600))
            self.api_domain = body.get("api_domain", self.api_domain)
            return self._token, self.api_domain

Callers use the returned api_domain for API requests and send the token in the Authorization header. If several processes need tokens, run this as a small internal service rather than copying the class into each one. Never log the token values themselves.

Rotation, revocation and offboarding

  • Keep an inventory: integration, client ID, authorising user, scopes, where the secret is stored, and the owner
  • Rotate client secrets and re-authorise on a schedule, and immediately if a secret may have leaked
  • Revoke tokens for integrations that are retired
  • Review the inventory when staff leave, especially if any integration was authorised under a personal account
  • Alert on repeated authentication failures, which often signal an invalidated token

Choosing the right client type

Zoho offers different client types in its API console. Server-based clients suit web applications where a user authorises access through a browser redirect. Self clients suit back-end scripts and services owned by your own organisation, where you generate the grant token in the console and exchange it once. Choose the type that matches how the integration runs, and record the choice in your inventory so future maintainers know how to re-authorise it.

Monitoring token health

Track token refreshes per integration, refresh failures, and any "Access Denied" or "INVALID_OAUTHTOKEN" errors. A sudden rise usually means a token was revoked, a limit was hit, or a second process is refreshing the same token. Catching it early avoids a quiet backlog of failed syncs. Include these figures on the same dashboard as your other integration health checks.

Related reading: API quota management across many integrations.

Testing the design

Test the token service in a sandbox organisation with its own clients and refresh tokens. Simulate expiry by forcing a refresh, run several callers at once to confirm only one refresh happens, and revoke a token to check that alerts fire and the error message is clear. These tests confirm the behaviour you will rely on when something goes wrong in production.

Token handling is one part of a reliable integration; our guide to Zoho CRM API limits covers credits and concurrency. If you are connecting several Zoho and external apps, our guide on when to hire a certified Zoho CRM consultant explains what a multi-app integration project should deliver.

Frequently asked questions

How long does a Zoho OAuth access token last?

One hour, per Zoho's token validity documentation. Cache it and reuse it until shortly before it expires.

Do Zoho refresh tokens expire?

No, they last until revoked. But Zoho stores up to 20 refresh tokens per user, and creating a 21st invalidates the oldest, which can break another integration.

Why do we get 'Access Denied' when refreshing tokens?

Usually because too many access tokens are generated: Zoho allows up to 10 from a refresh token in 10 minutes. Caching tokens centrally avoids it.

Should every integration use its own client?

Yes. One client per integration with minimum scopes limits the damage of a leak and makes revoking one integration safe.

Where should refresh tokens be stored?

In a secrets manager or encrypted store, never in config files, code or logs.

Secure your Zoho integrations with us

We design token management, scopes and secrets handling for multi-app Zoho ecosystems. See our CRM and API integration services, or talk to us about securing your integrations.

Related reading: CRM API integration.

About the Author

Manav Sharma is the founder of Insyrge and a CRM solutions architect. He works across Zoho, Salesforce, HubSpot, Odoo and more, designing the CRM setups, integrations and automations that keep sales and operations teams working from the same data.

Need Help Implementing This in Your Business?

Our certified Zoho consultants and automation experts can help you design and deploy custom workflows tailored to your operations.

Book Free Consultation