Zoho Books/Inventory/CRM REST API Capabilities and Rate Limits
What are the Zoho Books/Inventory/CRM REST API capabilities and per-minute rate limits?
TL;DR
Bottom line: Zoho Books (API v3) and Inventory (API v1) share a simple 100 requests/minute rate limit with daily caps by plan; Zoho CRM (API v7/v8) uses a credit-based system with concurrent call limits instead of per-minute caps. All three use OAuth 2.0 with 1-hour access tokens.
Key limit: Zoho Books/Inventory hard cap at 100 requests/minute/org with daily limits ranging from 1,000 (Free) to 10,000 (Premium+). Zoho CRM caps at 5,000 credits/day (Free) to unlimited (Ultimate/CRM Plus) with concurrent limits of 5-25 simultaneous calls.
Watch out for: Each Zoho product has independent API limits — Books, Inventory, and CRM quotas do not share pools. The 100/min Books/Inventory limit cannot be increased by upgrading to a higher plan.
Best for: SMB integrations with < 10,000 daily transactions across accounting, inventory, and CRM. For high-volume enterprise integrations, Zoho CRM's Bulk API (async, credit-based) is the only option exceeding per-minute caps.
Authentication: OAuth 2.0 via Zoho accounts server. Self Client (client_credentials) for server-to-server; Authorization Code for user-context. Access tokens last 1 hour; refresh tokens do not expire.
System Profile
This card covers the REST APIs for three core Zoho business applications — Zoho Books (accounting), Zoho Inventory (warehouse and order management), and Zoho CRM (customer relationship management). These three products share Zoho's OAuth 2.0 authentication infrastructure but have entirely separate API endpoints, rate limits, and capabilities. This card does NOT cover Zoho People, Zoho Desk, Zoho Projects, or other Zoho One suite products.
Zoho CRM — Credit-Based System (24h Rolling Window)
Edition
Base Credits
Per-User Credits
Max Credits/24h
Concurrent Calls
Sub-Concurrency
Free
5,000
—
5,000
5
10
Standard/Starter
50,000
+250/user
100,000
10
10
Professional
50,000
+500/user
3,000,000
15
10
Enterprise/Zoho One
50,000
+1,000/user
5,000,000
20
10
Ultimate/CRM Plus
50,000
+2,000/user
Unlimited
25
10
CRM API Credit Costs
Operation
Credit Cost
Notes
Standard metadata/user retrieval
1 credit
Org info, users, roles, profiles
Get Records (standard)
1 credit
Up to 200 records
Get Records via COQL (1-200)
1 credit
Query language interface
Get Records via COQL (201-1,000)
2 credits
Higher page sizes cost more
Get Records via COQL (1,001-2,000)
3 credits
Maximum COQL page size
Insert/Update/Upsert
1 credit per 10 records
Bulk within single request
Convert Lead
5 credits
Creates Contact + Account + Deal
Send Mail
20 credits
Email operations are expensive
Merge Records
50 credits
Deduplication merges
Bulk Read Initialize
50 credits
Async bulk export job
Bulk Write Initialize
500 credits
Async bulk import job
Mass Convert Leads
200 credits
Batch lead conversion
Authentication
All Zoho APIs authenticate via OAuth 2.0 through Zoho's accounts server. The token endpoint varies by data center region. [src5]
Flow
Use When
Token Lifetime
Refresh?
Notes
Self Client — Client Credentials
Server-to-server, backend jobs
Access: 1 hour
No refresh token; request new each hour
Recommended for integrations. grant_type=client_credentials
Self Client — Authorization Code
Backend scripts with user scopes
Access: 1 hour; Refresh: never expires
Yes — permanent refresh token
Generate code in API Console (3-min expiry)
Server-based App — Auth Code
Web apps with user interaction
Access: 1 hour; Refresh: never expires
Yes
Standard OAuth 2.0 with redirect URI
Client-side App
SPAs and mobile apps
Access: 1 hour
Limited
Uses implicit or PKCE flow
Regional Token Endpoints
Data Center
Accounts Server URL
US (.com)
https://accounts.zoho.com/oauth/v2/token
EU (.eu)
https://accounts.zoho.eu/oauth/v2/token
India (.in)
https://accounts.zoho.in/oauth/v2/token
Australia (.com.au)
https://accounts.zoho.com.au/oauth/v2/token
Japan (.jp)
https://accounts.zoho.jp/oauth/v2/token
Canada (.ca)
https://accounts.zohocloud.ca/oauth/v2/token
China (.com.cn)
https://accounts.zoho.com.cn/oauth/v2/token
Saudi Arabia (.sa)
https://accounts.zoho.sa/oauth/v2/token
Authentication Gotchas
Authorization header format is non-standard: Zoho uses Authorization: Zoho-oauthtoken {access_token} instead of the standard Bearer prefix for Books and Inventory APIs. CRM v7/v8 accepts both. [src3]
Organization ID required on every Books/Inventory call: Every request must include organization_id as a query parameter. Forgetting this returns a confusing generic error. [src3]
Scopes are product-specific: Books uses ZohoBooks.{module}.{permission}, Inventory uses ZohoInventory.{module}.{permission}, CRM uses ZohoCRM.{module}.{permission}. Cross-product scopes must be comma-separated. [src5]
Client credentials flow returns no refresh token: For grant_type=client_credentials, you get only a 1-hour access token. Use authorization code flow for permanent refresh tokens. [src5]
Data center mismatch causes silent auth failures: If your org is on EU but you hit the US token endpoint, authentication succeeds but API calls fail with 401 or wrong-org data. [src5]
Constraints
100 requests/minute is a hard cap for Books and Inventory — cannot be increased by upgrading plans. Only the daily cap scales.
Zoho CRM has no per-minute limit but has concurrent call caps — rate limiting is based on simultaneous connections (5-25) and daily credit budgets.
Sub-concurrency limit of 10 applies to all CRM editions — resource-intensive operations share a fixed pool of 10.
Books/Inventory APIs have no bulk endpoint — unlike CRM, all operations are synchronous and subject to the 100/min limit.
Cross-product API calls consume independent quotas — reading from CRM and writing to Books consumes both quotas independently.
Zoho CRM API v2.1 is on deprecation path — new features are v8-exclusive. No formal EOL date announced.
Webhook payload size limit — CRM webhook payloads are limited to 1 MB.
Access tokens expire after exactly 1 hour — no configurable lifetime across any Zoho product.
Integration Pattern Decision Tree
START — User needs to integrate with Zoho Books, Inventory, or CRM
├── Which Zoho product?
│ ├── Zoho Books (accounting)
│ │ ├── Daily volume < 1,000 records? → REST API (any plan)
│ │ ├── Need real-time sync? → REST API with 100/min throttling
│ │ └── Need >10K ops/day? → Not possible via API — use CSV import
│ ├── Zoho Inventory (warehouse/orders)
│ │ └── Same limits as Books (100/min, daily cap by plan)
│ └── Zoho CRM (customer data)
│ ├── < 2K records/op? → REST API with credit tracking
│ ├── > 2K records? → Bulk API (async CSV)
│ ├── Complex queries? → COQL API (SQL-like, 200-2K results)
│ └── Need notifications? → Webhooks or Notification API
├── Authentication type?
│ ├── Server-to-server → Self Client: client_credentials
│ ├── Backend + user context → Self Client: authorization code
│ └── Web/mobile app → Server-based or client-side flow
└── Error tolerance?
├── Zero-loss → retry queue + dead letter + idempotency
└── Best-effort → retry on 429 with exponential backoff
Verify: Calling limiter.request("GET", "/books/v3/invoices") returns JSON with code: 0.
Code Examples
Python: Sync Zoho Books invoices with pagination
# Input: access_token, organization_id
# Output: All invoices from Zoho Books (paginated)
import requests, time
TOKEN = "YOUR_ACCESS_TOKEN"
ORG_ID = "YOUR_ORG_ID"
BASE = "https://www.zohoapis.com/books/v3"
headers = {"Authorization": f"Zoho-oauthtoken {TOKEN}"}
def get_all_invoices():
all_invoices, page, has_more = [], 1, True
while has_more:
resp = requests.get(f"{BASE}/invoices", headers=headers,
params={"organization_id": ORG_ID, "page": page, "per_page": 200})
if resp.status_code == 429:
time.sleep(60); continue
data = resp.json()
all_invoices.extend(data.get("invoices", []))
has_more = data.get("page_context", {}).get("has_more_page", False)
page += 1; time.sleep(0.6) # stay under 100/min
return all_invoices
JavaScript/Node.js: Create CRM records with credit tracking
// Input: access_token, module name, records array
// Output: Created record IDs with credit usage estimate
// npm install axios@1
const axios = require("axios");
const TOKEN = process.env.ZOHO_ACCESS_TOKEN;
async function createRecords(module, records) {
const chunks = [];
for (let i = 0; i < records.length; i += 100) chunks.push(records.slice(i, i + 100));
const results = []; let totalCredits = 0;
for (const chunk of chunks) {
try {
const resp = await axios.post(`https://www.zohoapis.com/crm/v7/${module}`,
{ data: chunk },
{ headers: { Authorization: `Zoho-oauthtoken ${TOKEN}`, "Content-Type": "application/json" } });
results.push(...resp.data.data);
totalCredits += Math.ceil(chunk.length / 10);
} catch (err) {
if (err.response?.status === 429) { await new Promise(r => setTimeout(r, 5000)); continue; }
throw err;
}
}
console.log(`Created ${results.length} records, ~${totalCredits} credits`);
return results;
}
cURL: Quick API test across all three products
# Input: access_token, organization_id
# Output: Verification that auth works for all three APIs
TOKEN="YOUR_ACCESS_TOKEN"; ORG_ID="YOUR_ORG_ID"
# Test Zoho Books
curl -s "https://www.zohoapis.com/books/v3/organizations" \
-H "Authorization: Zoho-oauthtoken $TOKEN" | python3 -m json.tool
# Test Zoho Inventory
curl -s "https://www.zohoapis.com/inventory/v1/items?organization_id=$ORG_ID" \
-H "Authorization: Zoho-oauthtoken $TOKEN" | python3 -m json.tool
# Test Zoho CRM
curl -s "https://www.zohoapis.com/crm/v7/org" \
-H "Authorization: Zoho-oauthtoken $TOKEN" | python3 -m json.tool
Data Mapping
Cross-Product Field Mapping (Books <-> Inventory <-> CRM)
Books Field
Inventory Field
CRM Field
Type
Gotcha
contact_name
contact_name
Account_Name
String
CRM uses Account module; Books/Inventory use Contacts — different entity model
customer_id
contact_id
id
String (ID)
IDs are not shared across products — maintain cross-reference mapping
line_items[].item_id
line_items[].item_id
Product_Details[].product.id
String (ID)
Item IDs sync between Books and Inventory but NOT with CRM Products
total
total
Amount
Decimal
Currency formatting differs by product
date
date
Closing_Date
Date
All use YYYY-MM-DD but CRM datetime includes timezone offset
status
status
Stage
String (enum)
Status values differ completely — must map explicitly
currency_code
currency_code
Currency
ISO 4217
Multi-currency must be enabled independently in each product
Data Type Gotchas
Entity IDs are product-scoped: A contact ID in Books is completely different from the same contact's ID in CRM. Maintain a cross-reference table. [src3]
Date/datetime format inconsistency: Books and Inventory use YYYY-MM-DD. CRM uses YYYY-MM-DDTHH:mm:ss+HH:MM for datetime fields. [src3]
Multi-currency handling differs: Exchange rates are maintained independently in each product — they do not sync even in Zoho One. [src3]
Error Handling & Failure Points
Common Error Codes
Code
HTTP Status
Meaning
Resolution
44
429
Per-minute rate limit exceeded
Wait 60 seconds. Implement 600ms minimum gap between requests.
45
429
Daily rate limit exceeded
Wait until next day or upgrade plan.
1070
429
Concurrent call limit exceeded
Reduce parallelism. Free: max 5, Paid: max 10.
1000
500
Internal server error
Retry with exponential backoff.
1002
404
Resource not found
Verify resource ID. Check if record deleted.
—
401
Unauthorized
Refresh token. Verify Zoho-oauthtoken prefix.
INVALID_DATA
400
Malformed request body
Validate against API docs.
Failure Points in Production
Token expiry mid-batch: Access tokens expire after 1 hour. A batch processing 10K records at 100/min takes ~100 minutes. Fix: Request new token every 50 minutes proactively. [src5]
Organization ID missing causes cryptic errors: Books/Inventory return generic errors when organization_id is omitted. Fix: Always include organization_id. Store as config constant. [src3]
Data center mismatch after org migration: Hardcoded API URLs break if Zoho migrates your org. Fix: Use api_domain from token response, not hardcoded URLs. [src5]
Webhook delivery failures are not retried: CRM webhooks have no built-in retry. Fix: Log deliveries. Run reconciliation job to detect missed webhooks. [src8]
Concurrent limit hit during parallel processing: Running 15 threads against Standard CRM (10 concurrent limit) causes 429 errors. Fix: Use semaphore matching edition's concurrent limit. [src2]
Anti-Patterns
Wrong: Ignoring rate limits — sending requests as fast as possible
# BAD — will hit 100/min limit on Books/Inventory almost immediately
for invoice in invoices:
requests.post(f"{BASE}/invoices", headers=headers, json=invoice, params={"organization_id": org_id})
Correct: Implement request throttling with minimum gap
# GOOD — 600ms gap ensures < 100 requests/min for Books/Inventory
import time
for invoice in invoices:
requests.post(f"{BASE}/invoices", headers=headers, json=invoice, params={"organization_id": org_id})
time.sleep(0.6) # 600ms gap = max 100 requests/min
Wrong: Using Bearer prefix for Zoho Books/Inventory
# BAD — Books/Inventory require "Zoho-oauthtoken" prefix, not "Bearer"
curl -H "Authorization: Bearer 1000.xxxx" \
"https://www.zohoapis.com/books/v3/invoices?organization_id=12345"
Correct: Use Zoho-oauthtoken prefix
# GOOD — correct authorization header format
curl -H "Authorization: Zoho-oauthtoken 1000.xxxx" \
"https://www.zohoapis.com/books/v3/invoices?organization_id=12345"
Wrong: Hardcoding US data center domain for all orgs
# BAD — breaks for EU, India, Australia, Japan orgs
BASE_URL = "https://www.zohoapis.com"
TOKEN_URL = "https://accounts.zoho.com/oauth/v2/token"
Correct: Use data center domain from configuration
# GOOD — data center domain from config
import os
DC = os.environ.get("ZOHO_DC", "com") # com, eu, in, com.au, jp, ca
BASE_URL = f"https://www.zohoapis.{DC}"
TOKEN_URL = f"https://accounts.zoho.{DC}/oauth/v2/token"
Common Pitfalls
Assuming all Zoho APIs share rate limits: Books, Inventory, and CRM have independent pools. Track limits separately. Fix: Implement per-product rate limit tracking. [src1]
Not accounting for CRM credit costs: A single Bulk Write costs 500 credits. 10 bulk imports on Free plan (5K credits) exhausts the budget. Fix: Calculate credit costs before operations. Log cumulative usage. [src2]
Using CRM API v2.1 for new integrations: V2.1 receives no new features. Fix: Start new integrations on v7 or v8. [src7]
Forgetting the soid parameter in client_credentials: For multi-org setups, omitting soid defaults to primary org. Fix: Include soid={servicename}.{zsoid} in every token request. [src5]
Not handling pagination: Both Books/Inventory (200/page) and CRM (200/page) paginate by default. Fix: Always check has_more_page (Books/Inventory) or info.more_records (CRM) and loop. [src3]
Testing with admin credentials, deploying with limited-scope token: Fix: Define exact scopes during design. Test with production token scope. [src5]
Credit-based limits, Data Sharing APIs, Workflow APIs, OAS 3.0, Zia AI API
Zoho CRM
v7
2024-Q3
Supported
Improved webhook/notification APIs
Zoho CRM
v6
2024-Q1
Supported
Record locking, cadences, scoring rules
Zoho CRM
v2.1
2020
Deprecated (maintenance)
No new features. Migration recommended.
Zoho CRM
v1
2016
EOL
XML-based, fully retired
When to Use / When Not to Use
Use When
Don't Use When
Use Instead
SMB accounting integration with < 10K transactions/day
Enterprise-scale ERP with > 100K transactions/day
SAP, Oracle, NetSuite APIs with higher throughput
Zoho-centric ecosystem (Books + Inventory + CRM)
Need high-volume integration with many external systems
iPaaS middleware (Workato, Celigo) with Zoho connectors
Real-time individual record sync with < 100 ops/min
High-frequency data ingestion
Streaming platforms (Kafka, AWS EventBridge)
CRM data migration or bulk export
Books/Inventory bulk operations (no bulk API)
Zoho Books CSV import or scheduled REST batching
Server-to-server integration without user interaction
Mobile/SPA needing offline access
Zoho SDKs with PKCE flow
Cross-System Comparison
Capability
Zoho Books (v3)
Zoho Inventory (v1)
Zoho CRM (v7/v8)
Notes
Rate Limit Model
100/min + daily cap
100/min + daily cap
Credit-based + concurrent cap
CRM is most flexible
Max Daily (top tier)
10,000 requests
10,000 requests
Unlimited credits (Ultimate)
CRM scales much higher
Concurrent Calls (paid)
10
10
10-25 (by edition)
CRM Enterprise: 20, Ultimate: 25
Bulk API
None
None
Yes (async CSV)
Major gap for Books/Inventory
Webhook Support
No
No
Yes (Workflow Rules)
Books/Inventory lack push notifications
COQL/Query Language
No
No
Yes (SQL-like)
CRM-only feature
Pagination
page + per_page (200 max)
page + per_page (200 max)
page token (200 max)
Similar across all three
Auth Header
Zoho-oauthtoken
Zoho-oauthtoken
Zoho-oauthtoken or Bearer
CRM accepts both
API Versioning
Single (v3)
Single (v1)
Multiple (v2.1-v8)
CRM has rich version history
Error Format
code + message JSON
code + message JSON
code + details JSON array
CRM errors more structured
Modules/Endpoints
40+ (financial)
15+ (inventory)
50+ (CRM + custom modules)
Books has broadest financial coverage
Important Caveats
Rate limits are subject to change without notice. Zoho occasionally adjusts limits, especially for CRM credits. Always verify against current documentation.
The 100 requests/minute limit for Books and Inventory is an absolute ceiling that does not increase with any plan upgrade. Only the daily cap scales.
Zoho CRM's credit-based system was introduced in v8. Organizations on v7 or earlier may see different rate limiting behavior.
Zoho's built-in Books-CRM integration syncs contacts and invoices. If both APIs are called with conflicting data, native sync may overwrite API changes. Disable native sync for custom bidirectional integrations.
Free plan limits (1K/day for Books/Inventory, 5K credits for CRM) are extremely restrictive for production. Budget for at least Professional tier.