How to Export Data from Zoho Recruit: Methods & API Limits
Every method to export data from Zoho Recruit — UI exports, Bulk Read API, data backups — with exact record limits, API credit costs, and extraction strategies.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
How to Export Data from Zoho Recruit: Methods, API Limits & Migration Patterns
Zoho Recruit gives you four distinct ways to get data out: UI module exports, report exports, full data backups, and the API (including Bulk Read). Each method has different record limits, format options, and trade-offs. Which one you need depends on volume, whether you need attachments and notes, and whether this is a one-time extraction or a recurring sync.
This guide covers every extraction path with exact limits, credit costs, error handling, and the failure modes that cause migrations to stall.
The four ways to export data from Zoho Recruit
Zoho Recruit offers four official extraction methods, each suited to different use cases:
| Method | Max Records | Format | Includes Attachments? | Automation? |
|---|---|---|---|---|
| UI Module Export | 20,000 per module | CSV | No | No |
| Report Export | 20,000 per export | CSV, XLS, PDF | No | Scheduled |
| Data Backup | All records | CSV (zipped) | Yes | Scheduled |
| Bulk Read API | 200,000 per job | CSV (zipped) | No | Yes (async) |
No single method covers everything. A complete extraction — candidates, job openings, notes, attachments, and custom modules — almost always requires combining two or more of these.
How does the UI module export work?
Zoho Recruit's built-in Export Data feature lets you export data from individual modules in CSV format. Access it under Setup → Data Administration → Export, select a module (Candidates, Contacts, Clients, Job Openings, etc.), and click Export.
The hard limit is 20,000 records per module per export. If your Candidates module has 50,000 records, you cannot pull them all in a single export through the UI.
Workaround for the 20,000-record cap
Zoho's own support recommends creating custom views to export data in phases:
- Create a custom view with criteria filtering by record ID range (e.g., Candidate ID >= 1 AND Candidate ID <= 20000).
- Export that view.
- Adjust the range and repeat.
This works but is tedious and error-prone for large datasets. You're manually managing pagination through the UI, and any records created mid-export can fall through the cracks.
Warning: The UI export only covers structured field data. It does not include attachments, resumes as files, notes, or email history. Those require the API or a full data backup.
How to export reports from Zoho Recruit
Report exports give you more flexibility on which fields and cross-module data you pull. Navigate to Analytics → Reports, select a report, and export to Excel (.XLS), CSV, or PDF.
The limits to know:
- You can export up to 20,000 records per page in CSV format per export.
- Maximum of 5 reports of 20,000 records each per day.
- You cannot export the same report (of 20,000 records) twice in a day.
Report exports are useful for structured, filtered extractions — say, all candidates who applied in Q1 with specific statuses. At 100,000 records per day maximum throughput (5 × 20,000), this does not scale for full database extractions.
How does Zoho Recruit's data backup work?
Zoho Recruit Data Backup is a full account-level export that captures all module data, including attachments. It is designed for compliance and archival — maintaining a copy of your Recruit data to satisfy company and regulatory requirements.
To access it: Setup → Data Administration → Data Backup.
Key specifications:
- Available for all paid plans of Zoho Recruit.
- Free monthly backups by edition: Standard gets 1, Professional gets 1, Enterprise gets 2, Zoho One and People Plus get 2.
- Download links expire after 7 days.
- Backup duration ranges from minutes to hours depending on data volume. For accounts with tens of thousands of records and attachments, plan for overnight completion.
- Schedule options: one-time, every 2 weeks, or monthly.
- If you exhaust your monthly free backups, contact support@zohorecruit.com to request a manual backup.
Info: Data backups are the only built-in method that includes file attachments. If you need resumes, offer letters, or other documents alongside structured data, this is your starting point.
The backup is a blunt instrument. You get everything, not a filtered subset. For ATS migrations requiring relational links and field-level control, the Bulk Read API gives you filtering, field selection, and repeatable automation that the Data Backup cannot.
Zoho Recruit Bulk Read API: the large-scale extraction method
The Bulk Read API is Zoho Recruit's asynchronous bulk export endpoint that lets you extract up to 200,000 records per job without consuming standard per-record API credits. It is the correct tool whenever you need to export large volumes of structured data or take a programmatic backup without exhausting your API limits.
How the Bulk Read API works (step by step)
- Create a bulk read job — POST to
https://recruit.zoho.com/recruit/bulk/v2/readwith a JSON body specifying the module, fields, and optional criteria. - Poll the job status — GET to
/recruit/bulk/v2/read/{job_id}to check when the job completes. Alternatively, configure a callback URL in the job payload to receive a webhook notification on completion. - Download the result — GET to
/recruit/bulk/v2/read/{job_id}/resultto download a zipped CSV. This file is available for only 24 hours after the job completes.
Supported modules: Candidates, Job Openings, Clients, Interviews, Contacts, and Custom Modules.
Bulk Read API limits
| Constraint | Value |
|---|---|
| Max records per job | 200,000 |
| Pagination for >200k records | Set page: 2 to fetch records 200,001+ |
| Max download requests | 10 per minute (HTTP 429 if exceeded) |
| Result file availability | 24 hours after job completion |
| Max fields per request | 200 |
| Sorting / Group By | Not supported |
| API credits per job initialization | 50 |
What Bulk Read cannot export
This is where teams get surprised. The Bulk Read API does not support Notes, Attachments, Emails, or related and cross-module data. If you need candidate notes or file attachments, you must fall back to per-record REST API endpoints or use the Data Backup.
Zoho Recruit API credit system explained
API credits are Zoho Recruit's rate-limiting currency. API calls deduct credits from a rolling 24-hour budget based on operation type and intensity.
The credit budget per 24-hour rolling window:
| Edition | Formula | Max Credits |
|---|---|---|
| Free | 5,000 flat | 5,000 |
| Standard | 5,000 + (users × 250) | 100,000 |
| Professional | 10,000 + (users × 500) | 500,000 |
| Enterprise / Zoho One / People Plus | 15,000 + (users × 1,000) | 1,000,000 |
Credit costs for common extraction operations:
| Operation | Credits per call |
|---|---|
| Get Records (with cvid) | 3 |
| Get Deleted Records | 2 |
| Bulk Read Initialize | 50 |
| Get Attachments list | 1 |
| Download Attachment | 1 |
| Get Notes | 1 |
| All meta APIs (fields, modules, roles) | 1 |
The API credit limit uses a rolling 24-hour window from the time of the first call, not a midnight calendar reset. Plan extraction jobs accordingly — starting a large run at 11 PM does not reset your budget at midnight.
You can query your remaining credits before starting a large job:
GET /recruit/v2/org/credits
Exhausting your limit mid-migration returns 429 Too Many Requests and halts the extraction.
Concurrency limits matter more than credits for extraction speed
Zoho Recruit enforces concurrency limits — the maximum number of simultaneous API calls per user per application:
| Edition | Concurrency Limit |
|---|---|
| Free | 5 |
| Standard | 10 |
| Professional | 15 |
| Enterprise | 20 |
A sub-concurrency limit of 10 applies across all editions for: Get Records with sorting, Insert/Update of more than 10 records, and Search operations.
Even on Enterprise with 1 million daily credits, you can only have 20 API calls in flight simultaneously. For large-scale extractions involving thousands of attachment downloads, concurrency — not credits — is usually the actual throughput bottleneck. On Enterprise, downloading attachments at 20 concurrent calls, with each call taking ~0.5 seconds, yields a theoretical ceiling of roughly 40 files per second or ~144,000 files per hour — before factoring in retry overhead and network latency.
OAuth setup for API access
Every API call to Zoho Recruit requires a valid OAuth 2.0 access token. Access tokens expire after one hour. Any extraction script running longer than 60 minutes will fail silently without refresh logic.
Generating credentials
- Go to https://api-console.zoho.com and create a new Server-based Application.
- Note your Client ID and Client Secret.
- Authorize access by visiting:
https://accounts.zoho.com/oauth/v2/auth?scope=ZohoRecruit.modules.ALL,ZohoRecruit.bulk.ALL&client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REDIRECT_URI&access_type=offline - Exchange the returned
codefor an access token and refresh token:POST https://accounts.zoho.com/oauth/v2/token ?code=AUTH_CODE &client_id=CLIENT_ID &client_secret=CLIENT_SECRET &redirect_uri=REDIRECT_URI &grant_type=authorization_code - Store the refresh token securely. Use it to generate new access tokens as needed:
POST https://accounts.zoho.com/oauth/v2/token ?refresh_token=REFRESH_TOKEN &client_id=CLIENT_ID &client_secret=CLIENT_SECRET &grant_type=refresh_token
Scopes needed for full extraction: ZohoRecruit.modules.ALL (read access to all modules), ZohoRecruit.bulk.ALL (Bulk Read API), ZohoRecruit.settings.READ (field metadata).
Warning: Use a Zoho account with Administrator privileges for the OAuth authorization. Sub-administrator accounts may lack access to certain modules or field metadata endpoints.
API error reference
Common API errors during extraction and their specific causes:
| HTTP Code | Error | Cause | Fix |
|---|---|---|---|
| 400 | INVALID_DATA |
Malformed JSON body or invalid field name | Validate field names against /v2/settings/fields response |
| 400 | INVALID_CRITERIA |
Unsupported operator or field type in Bulk Read criteria | Check supported operators for the field's data type |
| 401 | INVALID_TOKEN |
Access token expired or revoked | Refresh the access token using the refresh token |
| 403 | NO_PERMISSION |
OAuth scope missing or account lacks module access | Add required scopes; verify admin privileges |
| 404 | ENTITY_NOT_FOUND |
Record ID does not exist or was deleted | Skip and log; check deleted records endpoint |
| 429 | API_LIMIT_EXCEEDED |
Credit budget exhausted | Wait for rolling window reset; query /org/credits |
| 429 | CONCURRENT_LIMIT_EXCEEDED |
Too many simultaneous calls | Implement a semaphore or connection pool |
How to export attachments and notes from Zoho Recruit
Neither the UI export nor Bulk Read includes attachments or notes. You need the REST API.
For attachments, extracting a candidate's resume is a two-step process per record:
- Get the Attachment ID — List all attachments for a specific record:
GET /recruit/v2/{module}/{record_id}/Attachments - Download the file — Use the returned Attachment ID to download the binary:
GET /recruit/v2/{module}/{record_id}/Attachments/{attachment_id}
The download endpoint returns a raw binary stream. Read the Content-Type and Content-Disposition headers to determine the file extension (e.g., .pdf, .docx) and write the binary data to disk or cloud storage. Do not trust the stored filename alone — the file extension in metadata sometimes does not match the actual file signature. Check the MIME type in response headers or read the first 4–8 bytes (magic bytes) to confirm file type before saving.
For notes: The Notes API at /recruit/v2/Notes retrieves notes across all supported modules — Candidates, Applications, Job Openings, Interviews, Clients, Contacts, Departments, and Custom Modules.
The complete multi-method extraction sequence
- Run Bulk Read jobs for each core module (Candidates, Job Openings, Clients, Contacts, Interviews, Custom Modules).
- Use the REST API to pull notes per module.
- For each record, list attachments and download files individually.
- Match everything by record ID.
Credit cost reality check for attachments
Attachment extraction is where budgets break. Example calculation for a 30,000-candidate database averaging 2 attachments each:
- List-attachment calls: 30,000 × 1 credit = 30,000 credits
- Download calls: 60,000 × 1 credit = 60,000 credits
- Total: 90,000+ credits for candidate attachments alone
On a Standard plan with 10 users (7,500 credits/day), that extraction takes 12+ days of API budget — before accounting for notes, job opening data, or any re-runs caused by errors.
Mitigation: Request a Data Backup first to obtain attachments in bulk without per-record API calls. Use the Bulk Read API for structured data where you need field-level control. Reserve REST API attachment calls for records that are missing from the backup or require targeted re-extraction.
Handling pagination in the REST API
Standard REST endpoints like /v2/Candidates return a maximum of 200 records per call. Paginate using page and per_page parameters. The response info object contains a more_records boolean indicating whether additional pages exist.
import requests
import time
def get_new_access_token(client_id, client_secret, refresh_token):
"""Refresh the OAuth access token."""
response = requests.post(
"https://accounts.zoho.com/oauth/v2/token",
params={
"refresh_token": refresh_token,
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "refresh_token"
}
)
return response.json().get("access_token")
def paginate_module(module, client_id, client_secret, refresh_token):
"""Paginate through all records in a Zoho Recruit module."""
access_token = get_new_access_token(client_id, client_secret, refresh_token)
token_refresh_time = time.time()
headers = {"Authorization": f"Zoho-oauthtoken {access_token}"}
base_url = f"https://recruit.zoho.com/recruit/v2/{module}"
page = 1
per_page = 200
more_records = True
all_records = []
while more_records:
# Refresh token if older than 55 minutes
if time.time() - token_refresh_time > 3300:
access_token = get_new_access_token(client_id, client_secret, refresh_token)
headers["Authorization"] = f"Zoho-oauthtoken {access_token}"
token_refresh_time = time.time()
params = {"page": page, "per_page": per_page}
response = requests.get(base_url, headers=headers, params=params)
if response.status_code == 200:
data = response.json()
records = data.get("data", [])
all_records.extend(records)
more_records = data.get("info", {}).get("more_records", False)
page += 1
time.sleep(0.5) # Stay within concurrency limits
elif response.status_code == 429:
error = response.json().get("code", "")
if error == "CONCURRENT_LIMIT_EXCEEDED":
time.sleep(5) # Brief pause; concurrent slot will free
else:
time.sleep(60) # Credit exhaustion; wait longer
elif response.status_code == 401:
access_token = get_new_access_token(client_id, client_secret, refresh_token)
headers["Authorization"] = f"Zoho-oauthtoken {access_token}"
else:
print(f"Error {response.status_code}: {response.text}")
break
return all_recordsThis pattern applies to any module endpoint. The token refresh logic at 55 minutes (3,300 seconds) provides a safe margin before the 60-minute expiry.
Field metadata extraction and picklist mapping
Custom fields are one of the most common migration failure points. Zoho Recruit allows extensive module customization, and picklist values rarely map 1-to-1 between systems.
Extracting field metadata
GET /recruit/v2/settings/fields?module=Candidates
The response returns an array of field objects. Key properties per field:
| Property | Description |
|---|---|
api_name |
The field name used in API requests and responses |
data_type |
Field type: text, picklist, date, lookup, boolean, etc. |
pick_list_values |
Array of {display_value, actual_value} objects for dropdown fields |
custom_field |
Boolean — true if this is a user-defined field |
mandatory |
Boolean — required fields in the target system must be populated |
Critical behavior: missing keys vs. null values
When a custom field is empty for a specific record, the Zoho API omits the key entirely from the JSON payload rather than returning null. Do not use response ["field_name"] directly — use response.get("field_name") or equivalent safe access. Without this, your transformation pipeline will throw KeyError exceptions on any record with an empty custom field.
Building a picklist mapping table
Before loading data into a target system, extract all picklist fields and build a translation table:
def extract_picklist_map(module, access_token):
"""Extract picklist values for all dropdown fields in a module."""
headers = {"Authorization": f"Zoho-oauthtoken {access_token}"}
response = requests.get(
f"https://recruit.zoho.com/recruit/v2/settings/fields?module={module}",
headers=headers
)
fields = response.json().get("fields", [])
picklist_map = {}
for field in fields:
if field.get("data_type") == "picklist":
picklist_map[field["api_name"]] = {
item["actual_value"]: item["display_value"]
for item in field.get("pick_list_values", [])
}
return picklist_mapZoho might store "In Progress" for an interview stage while your target system expects "First Round." Without this mapping step, picklist values either fail validation or load as incorrect data silently.
Third-party connectors for ongoing data sync
For recurring sync use cases (not one-time migration), purpose-built connectors are often more practical than custom API scripts:
| Tool | Zoho Recruit Support | Best For |
|---|---|---|
| Fivetran | Yes (connector available) | Warehouse sync; managed, no-code |
| Airbyte | Community connector | Self-hosted pipelines; open-source |
| Zapier | Yes (native integration) | Lightweight event-driven workflows |
| Zoho DataBridge | Native | Syncing Zoho Recruit with Zoho Analytics |
| Custom REST API | Always available | Full control; migration-grade extraction |
Fivetran and Airbyte normalize Zoho Recruit data into a relational schema automatically, which removes the manual ID-mapping work described below. Trade-off: they do not extract attachments as binary files, and their schema may not match your target ATS's import format.
Zoho DataBridge is Zoho's native sync layer for moving data into Zoho Analytics. It does not export data outside the Zoho ecosystem.
Zapier is appropriate for event-driven, low-volume workflows (e.g., new candidate created → notify Slack). It is not suitable for bulk historical extraction.
For ATS-to-ATS migration involving complete historical data transfer, custom API extraction remains the most reliable approach because it gives full control over field selection, filtering, relational ID mapping, and attachment handling.
Mapping relational ATS data during migration
An ATS is a relational database. A Candidate is linked to a Job Opening through an Application or Interview record. If you extract Candidates and Job Openings as flat CSVs without preserving linking IDs, you lose the entire history of who applied for what and when.
When processing API responses or Data Backup CSVs, track these core relationships:
- Candidate_ID — Unique identifier for the person.
- Job_Opening_ID — Unique identifier for the role.
- Interview record — Functions as a junction object, containing both
Candidate_IDandJob_Opening_ID.
Insert order for migration
When loading into a target system (Greenhouse, Lever, Workable, etc.), the insert sequence matters:
- Insert Job Openings first → generate new Job IDs in the target system.
- Insert Candidates → generate new Candidate IDs.
- Build a mapping table:
{zoho_old_id: target_new_id}for both entities. - Use the mapping table to translate IDs before inserting Interview or Application records.
Breaking this sequence produces orphaned records — candidates in your new ATS with no application history.
What Zoho Recruit won't let you export
Some data is partially or fully locked — understanding these gaps before starting a migration prevents scope surprises.
| Data Type | Exportable? | Method | Notes |
|---|---|---|---|
| Structured record fields | Yes | All methods | Core candidate, job, interview data |
| File attachments | Yes (indirect) | Data Backup or REST API | Binary files only via backup or per-record API |
| Notes | Yes | REST API only | /v2/Notes endpoint; not in Bulk Read |
| Email history | Partial | REST API per-record | Content may be limited; threading not preserved |
| Workflow/automation configs | No | None | Must be manually recreated |
| Blueprint stage definitions | No | None | Stage values on records export; definitions do not |
| Career site / portal settings | No | None | Not included in any export |
| Source attribution logic | Partial | Field values only | Source field exports; underlying logic does not |
| API v2 vs. v3 differences | — | — | Verify endpoint compatibility if Zoho updates API version |
When planning a migration away from Zoho Recruit, account for these gaps before scoping the project. Structured record data moves cleanly. System configuration and communication history do not.
Zoho Recruit API versioning
This guide covers the Zoho Recruit Bulk API v2 and REST API v2 endpoints. Zoho has not publicly announced a v3 API for Recruit as of this writing, but Zoho CRM has undergone version transitions that temporarily deprecated certain endpoints. Before building a production extraction pipeline:
- Verify endpoint availability at
https://www.zoho.com/recruit/developer-guide/apiv2/ - Check the
X-API-Versionresponse header on test calls to confirm which version is serving requests. - Monitor the Zoho Recruit developer changelog for deprecation notices.
If Zoho releases a new API version, the primary changes to watch for are: modified pagination behavior, updated credit costs, new or renamed field names in module responses, and changes to OAuth scope requirements.
Testing extraction scripts before running against production
Running an untested extraction script against a live production account risks exhausting API credits, triggering rate limit bans, or corrupting in-flight data.
Before production runs:
- Use the Zoho Recruit sandbox (available on Enterprise plans): a separate environment that mirrors your production configuration without affecting live data.
- Test with a single record: call each endpoint with one known record ID to validate response structure, field names, and authentication before looping over thousands of records.
- Validate credit consumption: make 5–10 test calls and query
/v2/org/creditsbefore and after to measure actual credit deduction per operation type. - Simulate 429 handling: artificially trigger a rate limit by rapid-firing calls in a test loop, then verify your retry logic recovers correctly.
- Check for missing keys: find a record where a custom field is empty and confirm your code handles the absent key without throwing an exception.
Common extraction mistakes and failure modes
Forgetting the 24-hour Bulk Read download window. Bulk Read results are only available for 24 hours after job completion. If your script does not download the ZIP promptly, you must re-run the job and spend another 50 credits.
Ignoring credit math for attachments. A full attachment export can consume multiple days of API budget. Calculate before starting: (records × average attachments per record × 2 calls) = total credits needed.
Assuming Data Backup completes quickly. Backup duration ranges from minutes to hours for large accounts. Schedule overnight and build in a polling check rather than assuming completion.
Not handling OAuth token expiry. Access tokens expire after one hour. Long-running scripts without refresh logic will return 401 INVALID_TOKEN errors mid-extraction and terminate silently if not caught.
Treating Zoho Recruit limits as identical to Zoho CRM. They share architectural DNA but have different limits. Zoho CRM recently increased its UI export limit to 200,000 records; Zoho Recruit's UI export remains capped at 20,000. CRM documentation does not reliably apply to Recruit.
Missing custom fields due to absent-key behavior. When a custom field is empty, Zoho omits the key from the JSON response entirely rather than returning null. All field access must use safe dictionary access methods (dict.get()), not direct key indexing.
Corrupted file types on attachment downloads. File extensions in stored metadata sometimes do not match actual file content. Check Content-Type response headers or inspect the first bytes of the file stream to determine true file type before saving.
Unmapped picklist values. Extract field metadata using /v2/settings/fields before transformation. Build a complete translation table for every picklist field before loading into the target system.
Missing the concurrent limit vs. credit limit distinction. Hitting CONCURRENT_LIMIT_EXCEEDED (429) means you need a brief pause (seconds) to free a concurrency slot. Hitting credit exhaustion (also 429) requires waiting for the rolling 24-hour window to reset. Your error handler must distinguish between these two 429 subtypes using the error code in the response body.
Decision framework: which export method to use
"I need a quick CSV of one module for analysis" → UI Module Export. Fast, no setup, limited to 20,000 records.
"I need filtered, cross-module data for reporting" → Report Export. 20,000 records per export, 5 per day maximum.
"I need a full copy of everything including attachments for compliance" → Data Backup. Monthly limits apply; plan for hours of processing time on large accounts.
"I'm migrating to another ATS and need complete structured data with field control" → Bulk Read API for core modules + REST API for notes + Data Backup for attachments. Budget 2–5 days of API credits for accounts over 30,000 candidates with attachments.
"I need ongoing automated data sync with another system" → REST API with webhooks or scheduled polling for low-to-medium volume; Fivetran or Airbyte for warehouse-destination pipelines. Zoho Recruit webhooks support module-level create/update/delete events but do not fire on note creation or attachment uploads — those require polling.
"I want to validate my migration scripts without touching production" → Enterprise sandbox environment for schema testing; single-record API calls against production for response structure validation before bulk runs.
Frequently Asked Questions
- What is the export limit in Zoho Recruit?
- The UI module export is capped at 20,000 records per module. Report exports also allow 20,000 records per export (max 5 per day). The Bulk Read API supports up to 200,000 records per job, with pagination for larger datasets.
- How do I export resumes and attachments from Zoho Recruit?
- Attachments cannot be exported through the UI or Bulk Read API. Use the REST API to list attachments per record (GET /v2/{module}/{record_id}/Attachments) then download each file individually. Alternatively, request a full Data Backup which includes attachments. Each attachment requires two API calls — one to list, one to download.
- What are Zoho Recruit API rate limits?
- Zoho Recruit uses a credit system with a 24-hour rolling window. Limits range from 5,000 credits (Free) to 1,000,000 credits (Enterprise), calculated by a base amount plus a per-user multiplier. It also enforces concurrency limits of 5–20 simultaneous calls per user per app, depending on edition.
- Can I export all data from Zoho Recruit at once?
- The Data Backup feature (Setup → Data Administration → Data Backup) exports all module data including attachments as a zipped set of CSVs. Paid plans get 1–2 free backups per month. Download links expire after 7 days. For selective exports, combine the Bulk Read API with per-record REST API calls for notes and attachments.
- Does the Zoho Recruit Bulk Read API export notes or attachments?
- No. The Bulk Read API does not support Notes, Attachments, Emails, or related/cross modules. Notes must be fetched separately using the GET /v2/Notes endpoint, and attachments require per-record API calls to list and download individually.