Skip to content

Monday CRM to Zoho CRM Migration: The CTO's Technical Guide (2026)

A technical guide to migrating from Monday CRM to Zoho CRM — covering API constraints, data model mapping, rate limits, and step-by-step execution for CTOs.

Raaj Raaj · · 20 min read
Monday CRM to Zoho CRM Migration: The CTO's Technical Guide (2026)
TALK TO AN ENGINEER

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

Migrating from Monday CRM to Zoho CRM is a data-model translation problem. Monday CRM stores everything as boards, items, columns, and Connect Boards links — a flexible, spreadsheet-like structure where relationships are opt-in. Zoho CRM enforces a relational schema with modules, lookup fields, and validated relationships between Accounts, Contacts, and Deals at the database level.

Every structural gap between these two architectures is where data silently disappears if you don't engineer around it.

If you need a fast answer: CSV export/import works for small, flat datasets where you accept manual relationship rebuilding. API-based migration is the only safe path when you need to preserve Account → Contact → Deal chains, activity history, and custom field integrity. Middleware tools like Zapier and Make handle ongoing single-record syncing — not bulk historical migration.

This guide covers the exact API constraints on both sides, a concrete object-mapping strategy, every realistic migration method with its trade-offs, and the edge cases that cause silent data loss.

For the reverse migration path, see Zoho CRM to Monday.com Migration: The CTO's Technical Guide. For general CRM migration strategy, see Best Practices for CRM Data Migration in 2026.

Monday CRM's Board Model vs. Zoho CRM's Relational Architecture

Monday CRM uses a board-based model. Your account ships with entity boards — Contacts, Leads, Deals, Accounts, and Activities — interconnected through Connect Boards columns. A contact links to an account through a Connect Boards column that connects to the Accounts entity board. These links are optional and unenforced — a Contact can exist without an Account, and a Deal can exist without a Contact.

Two monday-specific behaviors change migration scope fast:

  • Monday can auto-create or auto-associate Accounts from a contact's email domain, which inflates or fragments your Accounts count.
  • The Activities board does not contain the full interaction history — emails and standard notes are not logged there as activities. If you only export entity boards and ignore the Emails & Activities timeline, you will under-migrate history.

Zoho CRM is a traditional relational CRM. Standard modules — Leads, Contacts, Accounts, Deals, Tasks, Events, Calls, Products — have enforced schemas with validated field types and lookup relationships. A Contact's Account_Name field is a lookup to the Accounts module. A Deal's Contact_Name is a lookup to Contacts. These relationships are enforced at the module level. A Lead exists independently until conversion, at which point it splits into an Account, a Contact, and a Deal.

The architectural mismatch creates three specific migration risks:

  1. Relationship loss — Connect Boards links don't export to CSV. You must extract and rebuild them via API.
  2. Schema flattening — Monday's flexible columns (Status, Dropdown, People) don't map 1:1 to Zoho's typed fields (Picklist, Lookup, Owner).
  3. Activity orphaning — Monday's Emails & Activities timeline is item-scoped. Emails sent through Emails & Activities won't export as structured activity records.
Info

Monday's API is GraphQL-based, and personal token permissions mirror the user's UI access. If your Monday token user cannot see a private board, the extractor won't see it either. Zoho CRM uses OAuth 2.0 plus REST and Bulk APIs.

Migration Approaches: From CSV to Custom ETL

Native CSV Export/Import

How it works: Export each Monday CRM board to Excel/CSV, clean the data, then import into the corresponding Zoho CRM module via Setup → Data Administration → Import.

Monday export constraints: You may only export a board with up to 10,000 items at a time. Only table views can be exported. Monday CRM only exports columns visible in the current view — hidden columns won't appear. Connected board columns may export as blank on lower-tier plans. Subitem updates are not exported.

Zoho import constraints: Row limits vary by edition — Standard: 10,000; Professional: 20,000; Enterprise: 30,000; Ultimate: 50,000. File size limits: Enterprise and Ultimate: 25 MB; Professional and Standard: 10 MB; Free: 5 MB.

When to use it: Under 5,000 records, flat data, no relationships to preserve.

Complexity: Low | Scalability: Small datasets only

Warning

CSV exports from Monday CRM flatten Connect Boards links into text strings (or blanks). You'll need to manually rebuild Account → Contact → Deal relationships in Zoho using VLOOKUP-style matching on email or company name. For anything beyond a few hundred records, this becomes error-prone and time-consuming.

API-Based Migration (Custom Script)

How it works: Extract data from Monday's GraphQL API, transform it in your pipeline, then load into Zoho CRM's REST API or Bulk API.

Monday's API uses cursor-based pagination. You query boards, items, and column_values, paginating in pages of up to 500 items. Cursors are valid for 60 minutes after the initial request.

On the Zoho side, you can insert/update/upsert a maximum of 100 records per standard API call. For large datasets, use the Bulk Write API, which handles up to 25,000 records per CSV file with 200 column headers.

When to use it: Mid-to-large datasets, when you need to preserve relational links and have engineering resources.

Complexity: High | Scalability: Enterprise-ready with proper batching

Middleware/iPaaS (Zapier, Make)

How it works: Set up triggers in Monday CRM (item created/updated) that push data to Zoho CRM via pre-built connectors.

When to use it: Ongoing sync of new records between systems. These platforms are event-driven orchestration layers, not ordered historical loaders. They process records one at a time and hit rate limits quickly at scale — a poor primary mechanism for backfilling years of CRM history.

Complexity: Low–Medium | Scalability: Poor for bulk migration

Third-Party Migration Tools

Tools like Import2 offer point-and-click migration between Monday and Zoho. They handle basic field mapping and can automate pipeline and custom-field creation. Import2's documentation notes that custom objects and emails are not supported, and converted leads are not imported as converted leads. Evaluate whether they support your specific record types and custom fields before committing.

Complexity: Low–Medium | Scalability: Small-to-medium datasets

Comparison Table

Method Complexity Preserves Relationships Handles Custom Fields Scalability Best For
CSV Export/Import Low ❌ Manual rebuild Partial < 5K records Quick, flat migrations
API-Based (Custom) High ✅ Full control ✅ Full Enterprise Complex, relational data
iPaaS (Zapier/Make) Low–Med Partial Partial Poor for bulk Ongoing sync
Third-Party Tools Low–Med Partial Partial Small–Med Simple datasets
Managed Service Low (your side) ✅ Full ✅ Full Enterprise Any size, any complexity

Scenario recommendations:

  • Small business, flat data, < 2K records → CSV export/import
  • Mid-market, 2K–50K records, needs relationships → API-based or managed service
  • Enterprise, 50K+ records, custom objects, zero-downtime → Managed service or dedicated engineering team
  • Ongoing sync between both systems → iPaaS with webhook triggers

Monday CRM API Rate Limits

Monday's GraphQL API uses complexity-based rate limiting — 10,000,000 complexity points per minute per account. That sounds generous until you realize nested queries grow exponentially in complexity. A single query fetching 500 items with all column values from a board with 30+ columns can consume hundreds of thousands of complexity points. If you're extracting Contacts, Deals, Accounts, and Activities boards simultaneously, you will hit the limit.

Monday encourages pagination by adding limit arguments to your calls. Paginated calls typically have lower complexity than querying objects without limits. You can still get all the data, just with more calls.

Monday has also introduced daily call limits. Daily request limits are currently enforced for basic and standard accounts, with limits for pro and enterprise accounts being gradually rolled out.

Warning

Some third-party posts quote older Monday complexity ceilings. Verify rate limits against current official API documentation before planning throughput. The numbers have changed over time.

Zoho CRM API Credit System

API usage in Zoho CRM is calculated based on credits. Each API call costs at least 1 credit, but some operations cost more — a Convert Lead API call costs 5 credits. Credits are allocated based on your edition and user count. In the Standard edition with 10 user licenses, available credits are 52,500 (50,000 + 10 × 250). For Enterprise with 100 users, that's 115,000 credits per day.

Zoho also enforces concurrency limits — the maximum number of API calls simultaneously active per org per app. The sub-concurrency limit is 10 for all CRM editions.

The Bulk API Escape Hatch

Zoho's Bulk API bypasses standard credit limits for large-scale operations. For reads, 200,000 records can be exported in a single job. For writes, the maximum is 25,000 records per batch.

But the Bulk Write API has hard constraints:

  • Not supported: Attachments, Notes, file uploads, record images, Formula fields, Auto Number, Created By, Modified By, Created/Modified Time, rollup summary, Aggregate fields, Tags, and Campaign associations.
  • Initialization cost: Bulk Write initialization costs 500 credits per job whether it succeeds or fails.
  • Format requirement: You must upload a CSV file as a ZIP archive to a specific endpoint, obtain a file ID, then trigger an asynchronous job. You need polling logic to check job status and retrieve success/failure logs.
  • Lookup headers: For lookup fields in the CSV, headers must reference the field correctly — for example, Account_Name.id.

Attachments, notes, and file fields require separate API calls through the standard REST API — one call per file per record. For large attachment volumes, this burns hours and API credits.

Pre-Migration Planning Checklist

Before extracting a single record, audit your Monday CRM environment.

Data audit:

  • Count records per Monday board: Accounts, Contacts, Leads, Deals, Activities
  • Identify custom boards and non-standard columns
  • Flag unused/orphaned records for exclusion
  • Document all Connect Boards relationships
  • Export a sample set and verify field completeness
  • Flag Monday behaviors that distort counts: auto-account-association from email domains, qualified leads already moved into Contacts

Scope definition:

  • Which boards migrate? Which are archive-only?
  • Do Leads become Zoho Leads or Contacts? (Don't resurrect already-converted leads.)
  • Will you migrate historical activities, or start fresh?
  • Are there attachments/files to migrate?
  • Confirm your Zoho CRM edition supports the custom modules, fields, and linking modules you need

Strategy selection:

  • Big bang: Everything moves at once. Simpler, but higher risk. Requires a maintenance window.
  • Phased: Migrate by module in waves (Accounts first, then Contacts, then Deals). Lower risk, longer timeline.
  • Parallel run: Both systems live simultaneously during transition. See Why Running Two CRMs in Parallel Beats a Hard Cutover for the trade-offs.

For a complete audit framework, see The Ultimate CRM Data Migration Checklist.

Data Mapping: Translating Boards to Modules

The safest mental model: Monday boards are source containers; Zoho modules are target truth.

Object Mapping

Monday CRM Board Zoho CRM Module Notes
Accounts Accounts Direct mapping. Company name, domain, industry fields. Deduplicate by normalized name/domain first.
Contacts Contacts Map Account via lookup to Accounts module.
Leads Leads or Contacts Unconverted leads → Zoho Leads. Qualified leads already moved to Contacts → Zoho Contacts + Deal. Do not recreate already-converted leads as Leads.
Deals Deals (Potentials) Map stage, value, expected close. Link Contact and Account lookups.
Activities Tasks / Events / Calls Split by activity type. Not the full email history.
Emails & Activities notes/emails Zoho Notes Separate extraction path required.
Custom boards (Client Projects) Custom Module Subject to edition limits. Consider Zoho Creator for complex structures.

Field-Level Mapping

Monday CRM Column Column Type Zoho CRM Field Zoho Field Type Transform Required
Item Name Name Deal_Name / Last_Name Text None
Status Status (label) Stage Picklist Map label values to Zoho picklist values exactly
Deal Value Numbers Amount Currency Verify decimal precision
Owner People Owner Lookup (Users) Map Monday user emails to Zoho user IDs
Email Email Email Email None
Phone Phone Phone Phone None
Expected Close Date Date Closing_Date Date Standardize to YYYY-MM-DD
Account (Connect Boards) Connect Boards Account_Name Lookup Resolve Monday item IDs to Zoho Account IDs
Close Probability Numbers Probability Integer None
Priority Status/Dropdown Priority Picklist Map label values
monday item ID Internal ID External ID or custom text field Text Store source identifier for auditing

Handling Custom Objects and Modules

Custom modules are now available across Standard, Professional, Enterprise, and Ultimate editions, but with different limits:

  • Standard: 10 team modules, max 25 custom fields per team module
  • Enterprise / Zoho One: Up to 200 modules (custom + team combined), 300 custom fields per module
  • Ultimate / CRM Plus: Up to 500 modules, 500 custom fields per module

If your Monday CRM setup relies on non-standard boards (Client Projects, custom workflow boards), map these to Zoho custom modules. If you need many-to-many relationships or hierarchical structures deeper than two levels, consider Zoho Creator instead of custom CRM modules — standard Zoho CRM modules support lookup fields but not true many-to-many junction objects. Linking modules (junction objects) are only manipulable in Enterprise and above, and each requires two mandatory lookup fields.

External ID fields — useful for storing the source Monday item ID — are only available in Enterprise and Ultimate editions. On lower tiers, use a custom text field as a legacy key.

Handling Relationships

This is where most migrations fail silently. Monday's Connect Boards columns store item IDs that only make sense inside Monday. Your migration script must:

  1. Extract all items from both the source board (e.g., Contacts) and the linked board (e.g., Accounts)
  2. Build an ID mapping table: monday_item_id → zoho_record_id
  3. Insert parent records (Accounts) first
  4. Insert child records (Contacts) with the Zoho Account ID as the lookup value
  5. Insert grandchild records (Deals) with both Contact and Account lookups

Process dependencies in strict order: Accounts → Contacts → Deals → Activities. Loading child records before parent records exist causes silent relationship loss — the record imports, but the lookup field is null.

Step-by-Step Migration Execution

Phase 1: Extract from Monday CRM

Use the GraphQL API to pull all items from each entity board with cursor-based pagination:

query {
  boards(ids: [CONTACTS_BOARD_ID]) {
    items_page(limit: 500) {
      cursor
      items {
        id
        name
        column_values {
          id
          text
          value
        }
      }
    }
  }
}

Then paginate:

query {
  next_items_page(cursor: "CURSOR_FROM_PREVIOUS_RESPONSE") {
    cursor
    items {
      id
      name
      column_values {
        id
        text
        value
      }
    }
  }
}

Each cursor is valid for 60 minutes after the initial request. Build throttling logic that checks remaining complexity before each request.

Phase 2: Transform

  • Parse column_values: Monday returns column values as JSON blobs. The text field gives human-readable values; the value field gives raw JSON. Use value for structured data (dates, statuses, Connect Boards IDs).
  • Resolve Connect Boards links: Extract linked item IDs from the value JSON of Connect Boards columns. Build your ID mapping table.
  • Map picklist values: Monday Status/Dropdown labels must match Zoho picklist values exactly. Create a mapping dictionary. If your Monday board uses "In Progress" and your Zoho Deal Stage picklist expects "Negotiation/Review", the value imports as blank.
  • Normalize dates: Standardize all dates to YYYY-MM-DD.
  • Handle Owner assignment: Map Monday People column (user emails) to Zoho CRM user IDs via the Users API.
  • Deduplicate: Monday CRM allows duplicate items with the same name and email on the same board. Monday's auto-account-association can also create duplicate company records from email domains. Reconcile before import.
  • Split blended boards: If qualified leads were moved into the Contacts board in Monday, separate them into the correct Zoho target (Contacts + Deals, not Leads).

Phase 3: Load into Zoho CRM

For bulk loading, use the Bulk Write API:

import requests
import json
 
# Step 1: Upload CSV file as ZIP
upload_url = "https://content.zohoapis.com/crm/v8/upload"
headers = {"Authorization": f"Zoho-oauthtoken {access_token}"}
files = {"file": ("accounts.zip", open("accounts.zip", "rb"), "application/zip")}
upload_resp = requests.post(upload_url, headers=headers, files=files)
file_id = upload_resp.json()["details"]["file_id"]
 
# Step 2: Create Bulk Write Job
bulk_write_url = "https://www.zohoapis.com/crm/bulk/v8/write"
payload = {
    "operation": "upsert",
    "resource": [{
        "type": "data",
        "module": {"api_name": "Accounts"},
        "file_id": file_id,
        "find_by": "Account_Name",
        "field_mappings": [
            {"api_name": "Account_Name", "index": 0},
            {"api_name": "Website", "index": 1},
            {"api_name": "Industry", "index": 2},
            {"api_name": "Phone", "index": 3}
        ]
    }]
}
job_resp = requests.post(
    bulk_write_url,
    headers={**headers, "Content-Type": "application/json"},
    data=json.dumps(payload)
)
job_id = job_resp.json()["details"]["id"]
 
# Step 3: Poll for completion and retrieve success/failure logs

Capture the new Zoho Account IDs from the success logs. You need these to build relationships when loading Contacts and Deals.

Phase 4: Rebuild Relationships

After loading Accounts, load Contacts with Account lookup values. Then load Deals with both Contact and Account lookups:

# Update Contacts with Account lookups (batches of 100)
update_url = "https://www.zohoapis.com/crm/v8/Contacts"
for batch in chunked(contact_updates, 100):
    payload = {"data": [
        {"id": c["zoho_id"], "Account_Name": {"id": c["account_zoho_id"]}}
        for c in batch
    ]}
    requests.put(
        update_url,
        headers={**headers, "Content-Type": "application/json"},
        json=payload
    )

Finally, load Notes and Attachments via the standard REST API, since the Bulk Write API does not support them.

Phase 5: Validate

  • Compare record counts per module: Monday board item count vs. Zoho module record count
  • Sample 5–10% of records for field-level accuracy
  • Verify all lookup fields resolve correctly
  • Confirm picklist values imported without blanks
  • Check that activities are linked to correct Contacts/Deals
Tip

Build error logging into every API call. Log the Monday item ID, target module, lookup key, phase, raw vendor error, and retryability. This lets you replay failed records without re-running the entire migration.

Edge Cases That Cause Silent Data Loss

Duplicate records from auto-association: Monday CRM can auto-create Account records from a contact's email domain. This inflates or fragments your Accounts, creating duplicates that need reconciliation before you load into Zoho. Zoho's Bulk Write API can deduplicate on import using the find_by parameter — but only on one field. If you have duplicates across Leads and Contacts boards, reconcile them before import.

Multi-level relationship dependencies: Monday CRM's Account → Contact → Deal chain requires three separate load phases in strict dependency order. If you load Deals before Accounts exist in Zoho, the Account lookup fails silently — the Deal imports, but the relationship is null.

Multi-select Connect Boards: Monday allows you to link one Contact to multiple Accounts. Zoho CRM natively assumes a Contact belongs to one Account. You must enable the "Contact Roles" feature in Zoho or build a custom junction module. Linking modules are only available in Enterprise and above.

Attachments and notes: The Bulk Write API does not support Attachments, Notes, file uploads, or record images. You must use the standard REST API to upload each attachment individually — one API call per file per record. Zoho's total attachment size on a record should not exceed 100 MB.

Status/picklist mismatches: Monday Status labels are freeform text. If the label doesn't match a Zoho picklist value exactly, the field imports as blank. Map every picklist value before running the migration.

Subitem data: Monday subitems (child rows under items) don't export cleanly. Subitem updates are not exported to Excel — only parent item updates are. Via API, subitems are separate items that need a dedicated query.

Rich text updates: Monday's item updates often contain rich text, inline images, and @mentions. Zoho Notes support plain text or basic HTML. Sanitize Monday HTML payloads before pushing to Zoho.

Orphaned data: If an Account was deleted in Monday but the Contact remains, the Connect Boards column points to a ghost ID. Your ETL script must catch these and assign them to a default "Orphaned Records" account in Zoho.

Board item limits: The limit is 10,000 items per board for all plan types except Enterprise (100,000 items). CRM Pro users can have up to 5 boards, each with 100K items. If your boards are near these limits, API extraction takes significant time due to pagination and complexity limits.

Activities mismatch: Monday's Activities board excludes emails and standard notes. Activity migration and interaction-history migration are not the same thing. Plan the email/notes extraction path separately.

Zoho CRM Limitations to Plan For

  • Bulk Write field gaps: Formula fields, Auto Number, Created/Modified timestamps, Tags, and Campaign associations cannot be set via Bulk Write. Timestamps will reflect import time, not original creation time, unless you use a custom DateTime field.
  • Import automation trigger limit: Zoho only displays the option to trigger automation and process management if there are fewer than 10,000 rows in the import file. If your import exceeds 10K rows, workflows won't fire automatically.
  • Concurrency limits: Zoho limits simultaneous API calls per org per app. The sub-concurrency limit is 10 for all editions.
  • COQL query constraints: Zoho's COQL (CRM Object Query Language) is useful for audit queries but limited to 2,000 rows per call (100,000 with pagination), select-only, and does not support attachments in criteria.
  • Formula columns → Deluge: If your team relied on Monday's formula columns to calculate real-time metrics across boards, you'll rebuild these using Zoho CRM custom functions written in Deluge. This is a system implementation task, not a data migration task. Keep these phases separate.

Validation and Testing

  1. Record count comparison — Total items per Monday board must match total records per Zoho module (minus intentionally excluded records).
  2. Field-level spot check — Pull 50 random records from each module. Verify every field matches the source.
  3. Relationship verification — For every sampled Contact, confirm the Account lookup is correct. For every Deal, confirm both Contact and Account lookups resolve.
  4. Picklist audit — Export Zoho module data and check for blank picklist fields where Monday had values.
  5. UAT — Have sales reps validate 10 accounts end-to-end: Account details, linked Contacts, Deal history, and Activity logs.
  6. Rollback plan — Before go-live, take a full Zoho CRM backup. If validation fails, delete imported records and re-run after fixing the transform logic.

For broader QA patterns, see Best Practices for CRM Data Migration in 2026: The Engineer's Guide.

Post-Migration Tasks

Once data is verified, execute the final delta sync to catch any records created in Monday during the migration window.

  • Disable Monday creation rights. After the final sync, prevent new record creation in Monday CRM to stop data drift.
  • Rebuild automations. Monday CRM automations don't migrate. Monday's API does not expose board automation management. Recreate them as Zoho CRM Workflow Rules, Blueprints, or Deluge functions. Zoho Bulk Write does not trigger workflows, so automation parity is a separate implementation task.
  • Rebuild dashboards and views. Monday dashboards are board-specific and non-transferable. Build equivalent Zoho CRM reports and analytics.
  • Train users. Zoho CRM's module-based navigation is fundamentally different from Monday's board-based UI. Budget 1–2 weeks for team onboarding.
  • Monitor for inconsistencies. Run daily validation queries for the first 2 weeks. Look for orphaned records, blank lookups, and duplicate entries. Monitor deltas for at least one full business cycle after cutover.

When to Use a Managed Migration Service

Build in-house when your dataset is small, flat, and your team has spare engineering capacity. Hire a managed service when:

  • You have 50K+ records across multiple boards with Connect Boards relationships
  • Your team's engineering bandwidth is committed to product work
  • You need zero downtime — sales can't stop during migration
  • You have custom boards or non-standard column types that need transformation logic
  • Your Monday setup includes private workspaces, cross-board links, or files
  • You've already attempted a DIY migration and found orphaned relationships or missing data

The hidden cost of DIY isn't the API code — it's the edge cases. Handling picklist mismatches, resolving duplicate contacts, rebuilding multi-level relationships, and debugging silent import failures can easily consume 2–4 weeks of engineering time that was supposed to take "a few days."

Why ClonePartner for This Migration

ClonePartner has completed 1,500+ data migrations, including CRM-to-CRM paths with complex relational data. For a Monday CRM → Zoho CRM migration, our team handles:

  • Automated extraction with built-in throttling to manage Monday's complexity-point limits
  • Full relationship preservation — Account → Contact → Deal chains rebuilt programmatically, not via manual CSV matching
  • Bulk API integration with Zoho's Bulk Write API for high-throughput loading, bypassing daily credit limits
  • Custom object mapping for non-standard Monday boards that need Zoho custom modules
  • Zero-downtime cutover — your sales team continues working in Monday CRM while data syncs in the background

We typically complete migrations in days, not weeks. Every migration includes full validation reporting and error resolution before go-live.

Python Migration Script Outline

High-level structure for an API-based migration:

import requests
import json
import time
 
MONDAY_API_URL = "https://api.monday.com/v2"
ZOHO_API_URL = "https://www.zohoapis.com/crm/v8"
 
class MondayExtractor:
    def __init__(self, api_token):
        self.headers = {"Authorization": api_token, "Content-Type": "application/json"}
 
    def extract_board(self, board_id):
        """Extract all items from a board using cursor pagination."""
        all_items = []
        query = '''{ boards(ids: [%s]) { items_page(limit: 500) {
            cursor items { id name column_values { id text value } } } } }''' % board_id
        resp = requests.post(MONDAY_API_URL, json={"query": query}, headers=self.headers)
        data = resp.json()["data"]["boards"][0]["items_page"]
        all_items.extend(data["items"])
        cursor = data["cursor"]
 
        while cursor:
            next_query = '''{ next_items_page(cursor: "%s") {
                cursor items { id name column_values { id text value } } } }''' % cursor
            resp = requests.post(MONDAY_API_URL, json={"query": next_query}, headers=self.headers)
            page = resp.json()["data"]["next_items_page"]
            all_items.extend(page["items"])
            cursor = page["cursor"]
            time.sleep(1)  # Throttle to manage complexity budget
        return all_items
 
class ZohoLoader:
    def __init__(self, access_token):
        self.headers = {"Authorization": f"Zoho-oauthtoken {access_token}"}
 
    def upsert_records(self, module, records, find_by="Email"):
        """Insert/update records in batches of 100."""
        results = []
        for i in range(0, len(records), 100):
            batch = records[i:i+100]
            payload = {"data": batch, "duplicate_check_fields": [find_by]}
            resp = requests.post(
                f"{ZOHO_API_URL}/{module}/upsert",
                headers={**self.headers, "Content-Type": "application/json"},
                json=payload
            )
            results.append(resp.json())
            time.sleep(0.5)
        return results
 
class Transformer:
    def __init__(self, field_map, picklist_map, id_map):
        self.field_map = field_map       # monday_col_id -> zoho_field_api_name
        self.picklist_map = picklist_map  # {zoho_field: {monday_label: zoho_value}}
        self.id_map = id_map             # {monday_item_id: zoho_record_id}
 
    def transform_item(self, item):
        record = {}
        for col in item["column_values"]:
            zoho_field = self.field_map.get(col["id"])
            if not zoho_field:
                continue
            value = col["text"]
            if zoho_field in self.picklist_map:
                value = self.picklist_map[zoho_field].get(value, value)
            record[zoho_field] = value
        return record
 
# Execution order:
# 1. Extract Accounts -> Transform -> Load into Zoho Accounts
# 2. Build ID map: monday_account_id -> zoho_account_id
# 3. Extract Contacts -> Transform (resolve Account lookups) -> Load
# 4. Build ID map: monday_contact_id -> zoho_contact_id
# 5. Extract Deals -> Transform (resolve Contact + Account lookups) -> Load
# 6. Extract Activities -> Transform -> Load as Tasks/Events
# 7. Load Notes and Attachments via standard REST API
Info

This is a simplified outline. A production migration script needs OAuth token refresh logic, exponential backoff for rate limits, comprehensive error logging, a dry-run mode that validates transforms without writing to Zoho, and retry queues for failed records.

Best Practices

  • Back up everything first. Export your entire Monday account (Admin → General → Export account data) and take a Zoho CRM backup before touching anything.
  • Run a test migration. Migrate 100 records per module into a Zoho sandbox. Validate relationships and field values before touching production.
  • Validate incrementally. Don't wait until the end to check data quality. Validate after each module loads.
  • Disable Zoho automations during import. Workflow rules and Blueprints can fire during import and corrupt your data — auto-assigning owners, changing stages, or sending emails.
  • Document your field mapping. A shared spreadsheet mapping every Monday column to its Zoho equivalent (with data type and transform logic) prevents miscommunication between teams.
  • Store source IDs. Keep the Monday item ID in a Zoho custom field or External ID field. This makes auditing, debugging, and delta syncs far easier.

If your Monday CRM is truly small and flat, don't over-engineer this. If it's the operational memory of sales and revenue ops, don't under-engineer it either. The right migration path preserves relationships, makes rollback possible, and doesn't hand your team a month of cleanup disguised as a launch.

Frequently Asked Questions

Can I migrate from Monday CRM to Zoho CRM using CSV export?
Only for small, flat datasets under ~5,000 records. Monday's board export is capped at 10,000 items and strips Connect Boards relationships. You'll need to manually rebuild Account-Contact-Deal links in Zoho using VLOOKUP matching, which is error-prone at scale.
What are the Monday.com API rate limits for data extraction?
Monday's GraphQL API allows 10,000,000 complexity points per minute per account. Nested queries grow exponentially in complexity, so extracting large boards with many columns can exhaust this limit quickly. Use cursor-based pagination with 500 items per page and built-in throttling. Daily call limits are also being rolled out by plan tier.
Does the Zoho CRM Bulk Write API support attachments and notes?
No. Zoho's Bulk Write API does not support Attachments, Notes, file uploads, or record images. You must use the standard REST API to upload each attachment individually — one API call per file per record. Formula fields, Auto Number, and Created/Modified timestamps are also not supported via Bulk Write.
What order should I load data into Zoho CRM during migration?
Load in dependency order: Accounts first, then Contacts (with Account lookups), then Deals (with Contact and Account lookups), then Activities (linked to Contacts/Deals). Loading child records before parent records exist causes silent relationship loss — the record imports but the lookup field is null.
Can Zapier or Make handle a full Monday CRM to Zoho CRM migration?
They are better for ongoing sync or a parallel-run bridge, not bulk historical migration. These platforms process records one at a time via trigger-action automations, so using them as the primary migration engine for years of CRM history is usually the wrong fit.

More from our Blog