DocsAuthentication

Credentials

Authentication

OAuth tokens, scopes, data centers — and the token throttle that breaks integrations which construct a client per request.

Every request carries an OAuth 2.0 access token. The SDK obtains and refreshes that token for you from the client ID, client secret and refresh token you supply — but how often you construct the client determines whether that works or gets you blocked.

The access token throttle#

Zoho Accounts throws an access denied error when more than ten access tokens are generated within ten minutes using the same refresh token. Once that threshold is reached, Zoho Accounts blocks access token creation for the next ten minutes.

This matters specifically for SDK users. Whenever you initiate the client constructor, the existing access token is discarded and a new one is created on the next API call. A service that builds a client per request therefore mints a token per request, and crosses the limit after ten requests.

To avoid this, initiate the client object only once — during server startup, as a module-level value, or behind a singleton — and use that same instance throughout. The same access token is then used for all API calls. When the token expires after an hour, the SDK handles regeneration itself.

Reference: https://www.zoho.com/analytics/api/v2/authentication/refresh-token.html

What the failure looks like#

json
{
  "error": "access_denied",
  "error_description": "You have made too many requests continuously. Please try again after some time."
}

This comes from Zoho Accounts, not from the Analytics API, so it does not use the usual Analytics failure envelope and does not carry an Analytics errorCode. That is the signal you have hit the token throttle rather than an API error.

Do this#

csharp
public static class Analytics
{
    // Lazy<T> gives a thread-safe single instance for the whole process. The
    // access token it holds is reused and refreshed in place when it expires.
    private static readonly Lazy<IAnalyticsClient> _client =
        new Lazy<IAnalyticsClient>(() => new AnalyticsClient(
            Environment.GetEnvironmentVariable("ZOHO_CLIENT_ID"),
            Environment.GetEnvironmentVariable("ZOHO_CLIENT_SECRET"),
            Environment.GetEnvironmentVariable("ZOHO_REFRESH_TOKEN")));

    public static IAnalyticsClient Client => _client.Value;
}

// Everywhere else in the application:
IWorkspaceAPI workspace = Analytics.Client.GetWorkspaceInstance(orgId, workspaceId);

Not this#

csharp
// Every request mints a new access token. After ten requests inside ten
// minutes, Zoho Accounts blocks token creation for the next ten minutes.
public JsonElement HandleRequest(long orgId, long workspaceId)
{
    IAnalyticsClient ac = new AnalyticsClient(clientId, clientSecret, refreshToken);
    return ac.GetWorkspaceInstance(orgId, workspaceId).GetViews(null);
}

Refresh token limits#

LimitValueWhat happens when you cross it
Access tokens per refresh token10 per 10 minutesaccess_denied from Zoho Accounts; creation blocked for 10 minutes.
Access token lifetime1 hour (expires_in: 3600)The SDK regenerates it on the next call.
Refresh tokens per client, per user20Creating a 21st silently deletes the oldest, breaking whatever used it.

Refresh tokens do not expire on their own and do not change when used. Treat them as permanent credentials until explicitly revoked.

Scopes#

Request the union of scopes across every endpoint your integration calls. Prefer the narrowest set that works.

ScopeCovers
ZohoAnalytics.data.allReading and writing row data.
ZohoAnalytics.metadata.allViews, columns, folders, workspace structure.
ZohoAnalytics.modeling.allFormulas, query tables, alerts, schedules.
ZohoAnalytics.share.allSharing and permissions.
ZohoAnalytics.usermanagement.allUsers, roles, admins.
ZohoAnalytics.embed.allEmbed URLs, private links, publishing.
ZohoAnalytics.fullaccess.allEverything. Avoid unless you genuinely need it.

Each .all scope can be narrowed to .read, .create, .update or .delete.

Data centers#

An organization lives in exactly one data center. The API host, the OAuth accounts host and the token must all belong to that same region.

Data centerAPI hostOAuth accounts hostWeb application
United Statesanalyticsapi.zoho.comaccounts.zoho.comanalytics.zoho.com
Europeanalyticsapi.zoho.euaccounts.zoho.euanalytics.zoho.eu
Indiaanalyticsapi.zoho.inaccounts.zoho.inanalytics.zoho.in
Australiaanalyticsapi.zoho.com.auaccounts.zoho.com.auanalytics.zoho.com.au
Japananalyticsapi.zoho.jpaccounts.zoho.jpanalytics.zoho.jp
Chinaanalyticsapi.zoho.com.cnaccounts.zoho.com.cnanalytics.zoho.com.cn
Saudi Arabiaanalyticsapi.zoho.saaccounts.zoho.saanalytics.zoho.sa
Canadaanalyticsapi.zohocloud.caaccounts.zohocloud.caanalytics.zohocloud.ca
csharp
ac.SetAnalyticsServerURL("https://analyticsapi.zoho.eu");
ac.SetAccountsServerURL("https://accounts.zoho.eu");

Using a token you already hold#

If another part of your system manages tokens, hand the SDK an access token directly instead of letting it mint one.

csharp
// Responses are JsonElement; read them with the System.Text.Json API.
JsonElement views = workspace.GetViews(null);
foreach (JsonElement v in views.EnumerateArray())
{
    Console.WriteLine(v.GetProperty("viewName").GetString());
}

You are then responsible for refreshing it before it expires.

Authorisation is a separate check#

A valid token proves who is calling. Whether that user may perform the operation is checked separately against their organization role, workspace role and view permissions. Those failures return HTTP 403 with error code 7301, not a 401 — the token is fine; the user is not allowed.

  • Errors — the full failure envelope and the codes worth branching on.
  • Limits — API-side throttles, which are separate from this token limit.
  • Configuration — timeouts, proxies and custom headers.
Zoho Analytics C# SDK · REST API v2 Generated 2026-10-27