Skip to content

CSV to Ada Migration: A Technical Guide

A technical guide to migrating CSV data into Ada. Covers Knowledge API limits, encoding pitfalls, field mapping, transformation scripts, and the critical constraint that conversations can't be imported.

Wahab Wahab · · 18 min read
CSV to Ada Migration: A Technical Guide
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

CSV to Ada Migration: A Technical Guide

Last updated: August 2025. Covers Ada Platform API v2.

Migrating data from CSV files into Ada means transforming flat, delimiter-separated records into JSON payloads that Ada's Platform APIs can accept. Ada has no native CSV import feature. Every record — whether it's a knowledge article, an end-user profile, or a tag — must be converted to JSON and pushed through the corresponding REST endpoint. The process is straightforward for small knowledge bases but gets complicated fast when you're dealing with encoding issues, multi-language content, field mapping ambiguity, and Ada's specific API constraints.

This guide covers the full pipeline: CSV preparation, field mapping, transformation scripts, API constraints, and the failure modes you'll hit in production.

What Data Can You Actually Import into Ada from CSV?

Before you build anything, understand what Ada's APIs accept and what they don't. This is where most CSV-to-Ada migration plans go wrong — teams assume they can load everything from their CSV exports into Ada, and that's not the case.

What you can import:

  • Knowledge articles — via the Knowledge API (POST /api/v2/knowledge/bulk/articles/). Full read/write support, including bulk upsert.
  • Knowledge sources — via the Knowledge API (POST /api/v2/knowledge/sources/). Every article must belong to a source.
  • Tags — via the Knowledge API (POST /api/v2/knowledge/bulk/tags/). Labels for organizing articles. See the Tag Import section below for the full mapping and code example.
  • End-user profiles — via the End Users API (POST /v2/end-users/). Name, email, language, metadata, and sensitive metadata fields.

What you cannot import:

  • Historical conversations and messages. There is no endpoint that accepts historical conversation records. The Conversations API (POST /v2/conversations/) creates new, live conversations only — it does not accept historical data. If your CSV contains conversation transcripts, chat logs, or ticket histories, those records cannot be loaded into Ada.
  • Bot configuration, playbooks, or custom instructions from CSV. These must be configured through the Ada dashboard or their respective single-object API endpoints.
  • File attachments. Ada's attachment handling is for live conversation flows only, with a 50MB max and 7-day URL expiry.
Warning

The most common mistake in a CSV-to-Ada migration is assuming conversation history can be imported. It cannot. Plan for archival storage (data warehouse, S3, BigQuery) from the start. See our Ada to Ada migration guide for the full conversation constraint breakdown.

Ada's API Constraints You Need to Know Before Writing Code

Ada's Platform APIs are REST-based with JSON payloads. Every CSV row must be transformed before it can be sent. Here are the hard limits that will shape your migration script:

Authentication

All Ada Platform API requests require a Bearer token in the Authorization header. To obtain a Platform API token:

  1. Log in to your Ada dashboard.
  2. Navigate to Settings → Integrations → Platform API.
  3. Generate a new token. Store it securely — it is shown only once.

All requests use this header:

Authorization: Bearer YOUR_PLATFORM_API_TOKEN
Content-Type: application/json

A 401 Unauthorized response means the token is missing or malformed. A 403 Forbidden response means the token is valid but lacks permission for the requested endpoint — verify the token's scope in your Ada dashboard.

Knowledge API Limits

Constraint Limit
Requests per day 60,000
Requests per minute 1,000
Requests per second 200
Max articles per instance 50,000 (default; higher limits by plan)
Max article size 100KB
Max request payload 10MB
Supported content languages 9 (English, Arabic, Chinese, Dutch, French, German, Italian, Portuguese, Spanish)

End Users API Limits

Constraint Limit
Requests per day 60,000
Requests per minute 300
Requests per second 30

Data Export API Limits (for Extraction, Not Import)

Constraint Limit
Requests per second 10
Max page size 10,000 records
Max date range per query 60 days
Historical data availability 12 months

These numbers matter. If your CSV contains 60,000 end-user records, you'll need at minimum a full day to push them through at the 60,000 req/day cap — and that assumes zero errors or retries. At 200,000 end users, plan for 3–4 days of continuous API calls.

API Error Code Reference

HTTP Status Meaning Common Cause Fix
400 Bad Request Malformed payload or constraint violation Article count exceeds 50,000 cap; field exceeds character limit Validate payload before sending; check article count
401 Unauthorized Missing or invalid auth token Token not included or expired Verify Authorization: Bearer header; regenerate token
403 Forbidden Token lacks required permissions Token scope doesn't include target endpoint Check token permissions in Ada dashboard
404 Not Found Resource doesn't exist Referencing a source_id that hasn't been created Create the source before upserting articles
422 Unprocessable Entity Valid JSON but invalid field values Invalid language code; id exceeds 160 characters Validate field values before sending
429 Too Many Requests Rate limit exceeded Exceeded 60,000/day, 1,000/min, or 200/sec Respect Retry-After header; implement exponential backoff
5xx Server Error Ada-side failure Transient infrastructure issue Retry with exponential backoff; log for audit

CSV Preparation: Encoding, Delimiters, and the Problems Nobody Warns You About

CSV is a plain-text format with no built-in encoding declaration. Unlike JSON (which assumes UTF-8) or XML (which has an encoding header), a CSV file carries zero metadata about how its bytes should be interpreted. This is the single biggest source of silent data corruption in CSV-based migrations.

Encoding Traps

  • Excel's default CSV export uses Windows-1252, not UTF-8. Accented characters (é, ü, ñ), currency symbols (€, £), and non-Latin scripts will break when your Python script reads them as UTF-8.
  • Excel's "CSV UTF-8" export adds a BOM (Byte Order Mark) — three invisible bytes (EF BB BF) at the start of the file. Ada's API expects clean JSON, and if those BOM bytes leak into your first field value, the API will reject the payload or — worse — silently store corrupted data.
  • European locale CSVs use semicolons as delimiters because commas serve as decimal separators. If your source data was exported from a European-locale Excel instance, your parser will treat the entire row as a single column.

How to Fix It Before You Start

# Detect encoding
file -bi yourfile.csv
 
# Convert to UTF-8 without BOM
iconv -f WINDOWS-1252 -t UTF-8 input.csv > output.csv
 
# Strip BOM if present
awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' output.csv > clean.csv

In Python, use encoding='utf-8-sig' when reading to automatically strip the BOM:

import csv
 
with open('input.csv', 'r', encoding='utf-8-sig') as f:
    reader = csv.DictReader(f)
    for row in reader:
        # row keys are now clean, BOM-free
        pass
Tip

Always validate encoding before you run your transformation. Open a hex editor or run xxd input.csv | head to check the first bytes. If you see ef bb bf, you have a BOM. If you see characters above 7f that don't form valid UTF-8 sequences, you have a legacy encoding.

For a deeper look at when CSVs work and when they fall apart in migrations, see our post on using CSVs for SaaS data migrations.

Mapping CSV Columns to Ada's Data Model

Ada's Knowledge API expects a specific JSON structure. Your CSV columns won't map 1:1 — you'll need a transformation layer.

Step 1: Create a Knowledge Source

Every article must belong to a source. Create the source before attempting any article upsert. If you reference a source_id that doesn't exist, the API returns 404.

import requests
 
ADA_BASE_URL = "https://yourinstance.ada.support/api/v2"
ADA_TOKEN = "your-platform-token"
 
headers = {
    "Authorization": f"Bearer {ADA_TOKEN}",
    "Content-Type": "application/json"
}
 
source_payload = {
    "id": "src-csv-import",
    "name": "CSV Import - Help Center",
    "description": "Knowledge articles imported from CSV export"
}
 
resp = requests.post(
    f"{ADA_BASE_URL}/knowledge/sources/",
    headers=headers,
    json=source_payload
)
 
if resp.status_code == 201:
    print(f"Source created: {resp.json()['id']}")
elif resp.status_code == 409:
    print("Source already exists — safe to proceed with article upsert")
else:
    raise Exception(f"Source creation failed: {resp.status_code} {resp.text}")

Step 2: Knowledge Article Mapping

A typical CSV for knowledge articles might look like:

id,title,body,category,language,url
kb-001,How to reset password,"Click Settings > Security > Reset...",account,en,https://help.example.com/reset
kb-002,Billing FAQ,"We accept Visa, Mastercard...",billing,en,https://help.example.com/billing

Ada's bulk upsert endpoint expects this JSON structure:

{
  "source_id": "src-csv-import",
  "articles": [
    {
      "id": "kb-001",
      "name": "How to reset password",
      "content": "Click Settings > Security > Reset...",
      "language": "en",
      "metadata": {
        "category": "account",
        "source_url": "https://help.example.com/reset"
      }
    }
  ]
}

Key mapping decisions:

  • id must be unique and <= 160 characters. If your CSV uses numeric IDs, prefix them (e.g., kb-001) to avoid collisions with other sources.
  • name maps to your title column. Max 255 characters.
  • content is the article body. Max 100KB per article. If your CSV has articles exceeding this, you must split them before import.
  • language must be one of the 9 supported languages. Articles in unsupported languages (e.g., Japanese, Korean, Polish) will be accepted by the API but will not generate accurate generative answers — Ada applies machine translation for unsupported languages, and quality varies significantly.
  • Every article must belong to a source_id. Create the source first (see Step 1), then reference it in your bulk upsert.

Step 3: End-User Mapping

A CSV of end users might look like:

email,name,language,plan_type,account_id,user_token
jane@example.com,Jane Smith,en,enterprise,acct-1234,token-abc

The End Users API expects:

{
  "email": "jane@example.com",
  "name": "Jane Smith",
  "language": "en",
  "metadata": {
    "plan_type": "enterprise",
    "account_id": "acct-1234"
  },
  "sensitive_metadata": {
    "user_token": "token-abc"
  }
}

metadata vs. sensitive_metadata: Both accept arbitrary key-value pairs, but they differ in how Ada handles them:

  • metadata is stored and accessible in the Ada dashboard, in conversation transcripts, and via API responses. Use it for non-sensitive CRM or account attributes (plan type, account tier, locale).
  • sensitive_metadata is stored encrypted and is not surfaced in the Ada dashboard or conversation views. Use it for fields that should not be visible to Ada dashboard users — authentication tokens, PII, API keys, or any field your security policy requires to be restricted. The values are available to Generative Actions at runtime but are redacted in logs.
Danger

Critical timing constraint: End users created via the API that are not associated with a conversation within 24 hours are automatically deleted by Ada. If you're pre-loading end-user profiles before go-live, time your import accordingly — or plan to re-import immediately before cutover.

The Transformation Pipeline: CSV → JSON → Ada API

Here's a practical Python script skeleton for transforming CSV knowledge articles and pushing them to Ada's Knowledge API:

import csv
import json
import requests
import time
 
ADA_BASE_URL = "https://yourinstance.ada.support/api/v2"
ADA_TOKEN = "your-platform-token"
SOURCE_ID = "src-csv-import"
BATCH_SIZE = 50  # articles per request; keep well under 10MB
 
def read_csv(filepath):
    articles = []
    with open(filepath, 'r', encoding='utf-8-sig') as f:
        reader = csv.DictReader(f)
        for row in reader:
            article = {
                "id": row["id"].strip(),
                "name": row["title"].strip()[:255],
                "content": row["body"].strip(),
                "language": row.get("language", "en").strip(),
                "metadata": {
                    k: v for k, v in row.items()
                    if k not in ("id", "title", "body", "language")
                }
            }
            # Validate article size
            if len(json.dumps(article).encode('utf-8')) > 102400:
                print(f"WARNING: Article {article['id']} exceeds 100KB — skipping")
                continue
            articles.append(article)
    return articles
 
def bulk_upsert(articles, source_id):
    headers = {
        "Authorization": f"Bearer {ADA_TOKEN}",
        "Content-Type": "application/json"
    }
    for i in range(0, len(articles), BATCH_SIZE):
        batch = articles[i:i + BATCH_SIZE]
        payload = {
            "source_id": source_id,
            "articles": batch
        }
        # Check payload size
        payload_bytes = len(json.dumps(payload).encode('utf-8'))
        if payload_bytes > 10_000_000:
            print(f"Payload exceeds 10MB at batch {i}. Reduce BATCH_SIZE.")
            continue
 
        resp = requests.put(
            f"{ADA_BASE_URL}/knowledge/bulk/articles/",
            headers=headers,
            json=payload
        )
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 60))
            print(f"Rate limited. Waiting {retry_after}s...")
            time.sleep(retry_after)
            # Retry the same batch
            resp = requests.put(
                f"{ADA_BASE_URL}/knowledge/bulk/articles/",
                headers=headers,
                json=payload
            )
        if resp.status_code not in (200, 201):
            print(f"Error at batch {i}: {resp.status_code} {resp.text}")
        else:
            print(f"Upserted batch {i}{i+len(batch)}")
        time.sleep(0.01)  # Stay under 200 req/sec
 
articles = read_csv("knowledge_articles.csv")
bulk_upsert(articles, SOURCE_ID)

This script handles the core path. In production, add:

  • Exponential backoff with jitter for 429 responses (not just a flat Retry-After).
  • Idempotency: Ada's bulk upsert is idempotent on the id field — re-running the script will update existing articles rather than create duplicates. This makes it safe to retry failed runs in full.
  • A dry-run mode that validates all rows without sending API calls.
  • Logging of every API response (status code, response body, batch index) for post-migration audit.

Importing Tags from CSV

Tags are listed in Ada's Knowledge API under POST /api/v2/knowledge/bulk/tags/. The CSV-to-tag mapping is simpler than articles but follows the same bulk structure.

A typical tags CSV:

tag_id,tag_name,tag_color
tag-001,billing,blue
tag-002,account,green
tag-003,technical,red

Ada's bulk tag upsert payload:

{
  "tags": [
    { "id": "tag-001", "name": "billing", "color": "blue" },
    { "id": "tag-002", "name": "account", "color": "green" },
    { "id": "tag-003", "name": "technical", "color": "red" }
  ]
}

Example tag import script:

def import_tags(filepath):
    tags = []
    with open(filepath, 'r', encoding='utf-8-sig') as f:
        reader = csv.DictReader(f)
        for row in reader:
            tags.append({
                "id": row["tag_id"].strip(),
                "name": row["tag_name"].strip(),
                "color": row.get("tag_color", "").strip() or None
            })
 
    headers = {
        "Authorization": f"Bearer {ADA_TOKEN}",
        "Content-Type": "application/json"
    }
    resp = requests.post(
        f"{ADA_BASE_URL}/knowledge/bulk/tags/",
        headers=headers,
        json={"tags": tags}
    )
    if resp.status_code not in (200, 201):
        print(f"Tag import failed: {resp.status_code} {resp.text}")
    else:
        print(f"Imported {len(tags)} tags")
 
import_tags("tags.csv")

Tags must be imported before articles if you plan to associate tags with articles at upsert time. If you're adding tags to existing articles post-import, the article update endpoint (PUT /api/v2/knowledge/articles/:id) accepts a tag_ids array.

Handling Knowledge Article Content from CSV

CSV is a poor format for rich text. If your knowledge base articles contain HTML, Markdown, or structured formatting, you'll hit these issues:

Commas and newlines inside content fields: A knowledge article body will almost certainly contain commas and line breaks. Your CSV must use proper quoting (double-quote delimiters with escaped inner quotes). Most CSV libraries handle this correctly, but manually edited CSVs often don't.

HTML entities: If your source system exported HTML content, it may contain entities like &amp;, &lt;, &#x27;. Decide whether to pass these through as-is (Ada will store them) or decode them before import.

Content size: Ada enforces a 100KB per-article limit. A single long FAQ document exported as one CSV row could exceed this. Check sizes programmatically and split oversized articles before attempting the upsert.

Content language: Ada's Knowledge API supports articles in 9 languages: English, Arabic, Chinese, Dutch, French, German, Italian, Portuguese, and Spanish. If your CSV includes articles in other languages, they'll be accepted by the API but may not generate accurate responses — Ada uses machine translation for unsupported languages, and quality varies.

Migrating End Users from CSV to Ada

End-user imports from CSV are conceptually simple but operationally tricky because of the 24-hour auto-deletion rule.

The workflow:

  1. Parse your CSV, mapping each row to Ada's end-user schema (email, name, language, metadata, sensitive_metadata).
  2. Call POST /v2/end-users/ for each user. There is no bulk endpoint — it's one request per user.
  3. Respect the 300 req/min and 60,000 req/day limits.

Time estimate: 60,000 end users = 1 full day at max throughput. 200,000 end users = 3–4 days minimum. 500,000+ end users = over a week of continuous API calls.

The 24-hour auto-deletion constraint means you should not pre-load end users weeks before go-live. Time your end-user import to complete within 24 hours of when those users will start conversations. If you're using Ada's embed or SDK, end users get created automatically on first interaction — making a bulk pre-load unnecessary for many use cases.

Paginating GET Requests During Validation

After importing end users, validate by pulling samples via GET /v2/end-users/. This endpoint is paginated. Use limit and offset (or cursor-based pagination if supported by your Ada version) to walk through large result sets:

def fetch_end_users(limit=100):
    headers = {"Authorization": f"Bearer {ADA_TOKEN}"}
    offset = 0
    all_users = []
 
    while True:
        resp = requests.get(
            f"{ADA_BASE_URL.replace('/api/v2', '')}/v2/end-users/",
            headers=headers,
            params={"limit": limit, "offset": offset}
        )
        data = resp.json()
        users = data.get("results", [])
        all_users.extend(users)
        if not data.get("next"):
            break
        offset += limit
        time.sleep(0.1)  # Respect rate limits on GET requests
 
    return all_users

Compare the returned count against your CSV row count (minus known skips) to confirm completeness.

What About Conversation Data in Your CSV?

If you have conversation transcripts, chat logs, or ticket exports in CSV format, here's the definitive answer: you cannot import them into Ada.

Ada's Conversations API is designed for creating and managing live conversations. There is no historical import endpoint. The typical approach:

  1. Archive to a data warehouse. Transform the CSV into a structured format (Parquet, BigQuery table, PostgreSQL) and keep it accessible for compliance and reference.
  2. Build a lookup integration. If agents need to reference historical conversations during live Ada interactions, build a Generative Action that queries your archive and surfaces relevant history.
  3. Accept the break. For reporting, treat the migration date as day zero. Historical metrics stay in the old system; new metrics start in Ada.

For a deeper walkthrough of conversation archival strategies, see our help desk data mapping guide.

Validation Checklist: Before and After the Import

If you are migrating a public-facing help center, you also need to plan for URL redirects and SEO preservation. For a complete look at those requirements, see our knowledge base migration checklist.

Pre-Import Validation

  • Encoding confirmed as UTF-8 (no BOM, no Windows-1252 artifacts)
  • Delimiter verified (comma vs. semicolon vs. tab)
  • No article exceeds 100KB when serialized to JSON
  • All article IDs are unique and <= 160 characters
  • All article names are <= 255 characters
  • Every article has a valid language value from the 9 supported languages
  • Knowledge source created in Ada before bulk upsert (POST /api/v2/knowledge/sources/)
  • Tags imported before articles if tag associations are needed at upsert time
  • Total article count is under 50,000 (or your plan's custom limit)
  • End-user email fields are valid and deduplicated
  • sensitive_metadata fields identified and separated from standard metadata
  • No conversation data in the import set (it will fail silently or be misinterpreted)

Post-Import Validation

  • Article count in Ada matches CSV row count (minus skipped/errored rows)
  • Spot-check 5–10 articles in Ada's dashboard for content accuracy, encoding, and formatting
  • Test generative answers — ask the AI agent questions that should be answered by imported articles. Allow 5 minutes for indexing after the final upsert.
  • Verify end-user profiles — pull a paginated sample via GET /v2/end-users/ and compare against CSV source
  • Verify tags — pull tag list via GET /api/v2/knowledge/tags/ and compare against tags CSV
  • Check rate limit usage in your logs to confirm no requests were silently dropped

Common Failure Modes

Failure Cause Fix
400 on bulk upsert Article limit exceeded (50,000 cap) Check current article count; delete unused articles or request limit increase
401 Unauthorized Missing or malformed auth token Verify Authorization: Bearer header; regenerate token in Ada dashboard
403 Forbidden Token lacks endpoint permissions Check token scope in Ada dashboard Settings → Integrations
404 Not Found on article upsert source_id doesn't exist Create source via POST /api/v2/knowledge/sources/ before upserting articles
422 Unprocessable Entity Invalid field value Check language is one of 9 supported codes; check id <= 160 chars
Garbled characters in article content CSV encoding mismatch (Windows-1252 read as UTF-8) Convert to UTF-8 before processing; use encoding='utf-8-sig' in Python
First article's id field has invisible prefix BOM bytes prepended to first CSV cell Strip BOM during preprocessing
End users disappearing after import 24-hour auto-deletion for users without conversations Time import closer to go-live; or rely on auto-creation via embed
429 Too Many Requests mid-import Exceeded 60,000/day or 300/min rate limit Implement backoff with jitter; spread import across multiple days
Articles not appearing in AI responses Indexing latency (up to 5 minutes) Wait at least 5 minutes after final upsert before testing
Entire row imported as single field Semicolon delimiter from European CSV export Explicitly set delimiter in your CSV reader: csv.DictReader(f, delimiter=';')
Tags not associated with articles Tags imported after articles, or tag_ids not included in upsert payload Import tags first; include tag_ids array in article upsert or update articles post-import

When CSV Is the Wrong Starting Point

CSV works for loading knowledge articles and end-user profiles into Ada — but it's the wrong tool in several scenarios:

  • You need to preserve relationships between conversations, messages, and end users. CSVs flatten relational data. Use Ada's Data Export API (JSON) as your source instead.
  • Your data contains rich formatting, embedded images, or attachments. CSV strips all of this. If your knowledge base has inline screenshots or downloadable PDFs, you need a different pipeline.
  • You're migrating from another platform with an API. Pulling data via API and pushing to Ada's API directly (JSON-to-JSON) avoids the entire class of CSV encoding and parsing issues. It's more work to set up but dramatically more reliable at scale.
  • Your dataset exceeds 50,000 articles. You'll need to coordinate with Ada's team for a higher limit, and at that volume, API-to-API is almost always faster and more reliable than CSV intermediation.

For a comparison of CSV vs. API vs. JSON export approaches, see Using CSVs for SaaS Data Migrations.

Keeping Data in Sync After the Initial Import

A migration isn't done when the initial load completes. If your knowledge base is maintained in an external system (a CMS, a wiki, a shared drive of CSVs), you need an ongoing sync strategy.

Ada's Knowledge API supports full upsert semantics — if an article with the same id already exists, it gets updated. This means you can re-run your CSV-to-Ada pipeline on a schedule to pick up changes. But there are caveats:

Deleted articles aren't handled by upsert. If you remove a row from your CSV, the corresponding Ada article remains. You need an explicit deletion step that diffs your CSV against Ada's article list and calls DELETE /api/v2/knowledge/articles/:id for removed articles. Here's a working implementation:

def sync_deletions(csv_filepath, source_id):
    """Remove Ada articles that no longer exist in the CSV."""
    # Build set of IDs from current CSV
    csv_ids = set()
    with open(csv_filepath, 'r', encoding='utf-8-sig') as f:
        reader = csv.DictReader(f)
        for row in reader:
            csv_ids.add(row["id"].strip())
 
    # Fetch all article IDs currently in Ada for this source
    headers = {"Authorization": f"Bearer {ADA_TOKEN}"}
    ada_ids = set()
    offset = 0
    limit = 1000
 
    while True:
        resp = requests.get(
            f"{ADA_BASE_URL}/knowledge/articles/",
            headers=headers,
            params={"source_id": source_id, "limit": limit, "offset": offset}
        )
        data = resp.json()
        articles = data.get("results", [])
        for article in articles:
            ada_ids.add(article["id"])
        if not data.get("next"):
            break
        offset += limit
        time.sleep(0.05)
 
    # Delete articles present in Ada but absent from CSV
    to_delete = ada_ids - csv_ids
    print(f"Articles to delete: {len(to_delete)}")
 
    for article_id in to_delete:
        resp = requests.delete(
            f"{ADA_BASE_URL}/knowledge/articles/{article_id}/",
            headers=headers
        )
        if resp.status_code == 204:
            print(f"Deleted: {article_id}")
        else:
            print(f"Failed to delete {article_id}: {resp.status_code} {resp.text}")
        time.sleep(0.01)  # Stay under rate limits
 
sync_deletions("knowledge_articles.csv", SOURCE_ID)

Additional sync caveats:

  • Indexing latency applies to every update. Bulk-updated articles take up to 5 minutes to reflect in generative answers. If you're updating frequently, stagger updates to avoid serving stale content.
  • Consider Ada's native integrations first. Ada has out-of-the-box integrations with Zendesk Guide, Salesforce Knowledge, Freshworks, Contentful, and others that handle sync automatically. If your knowledge lives in one of those systems, a direct integration is almost always preferable to a CSV pipeline.

Planning a Zero-Downtime Cutover

If you're migrating to Ada while keeping an existing support system running, the knowledge import from CSV can happen entirely in the background — it doesn't affect live operations. End-user imports need more careful timing due to the 24-hour auto-deletion rule.

The real downtime risk comes from the cutover: the moment you switch live traffic to Ada. Plan for a brief parallel-run period where both systems are active. For detailed strategies on managing this, see our guide on zero-downtime help desk migration.

When to Seek Additional Help

A CSV-to-Ada knowledge import with under 5,000 articles and clean, well-encoded data is a reasonable DIY project for a team with Python experience. Budget a day for scripting, a day for testing, and a half-day for validation.

The complexity increases significantly when:

  • Your CSV has encoding problems or inconsistent formatting from multiple export sources — each source may require a separate transformation layer
  • You're importing both knowledge articles and end users and need coordinated timing to avoid the 24-hour auto-deletion window
  • Your article count approaches or exceeds the 50,000 cap and requires coordination with Ada's team
  • You need an ongoing sync pipeline (not just a one-time load), including the deletion-diffing logic
  • You have conversation data that needs archival alongside the Ada migration, requiring a separate data warehouse pipeline

ClonePartner has handled 1,500+ migrations including complex CSV-to-platform data loads with encoding normalization, field mapping, and API orchestration. If the scope is bigger than a weekend project, we can get it done in days.

Frequently Asked Questions

Can I import CSV data directly into Ada?
No. Ada has no native CSV import feature. You must convert CSV rows to JSON and use Ada's REST APIs — the Knowledge API for articles and the End Users API for user profiles. Each API call uses JSON payloads, not CSV.
Can I import conversation history into Ada from a CSV?
No. Ada's Conversations API only creates new, live conversations. There is no endpoint that accepts historical conversation data. Conversation transcripts from CSVs must be archived externally (e.g., in a data warehouse).
What are Ada's Knowledge API rate limits for bulk imports?
Ada's Knowledge API allows 60,000 requests per day, 1,000 per minute, and 200 per second. The max request payload is 10MB, each article can be up to 100KB, and the default article cap is 50,000 per instance.
How do I handle CSV encoding issues when migrating to Ada?
Convert your CSV to UTF-8 without BOM before processing. Excel's default export uses Windows-1252, which corrupts accented characters. In Python, use encoding='utf-8-sig' to auto-strip the BOM. Verify encoding with `file -bi yourfile.csv` on the command line.
How long does a CSV to Ada end-user migration take?
Ada's End Users API allows 60,000 requests per day with no bulk endpoint. Each user requires one API call. So 60,000 users take about 1 day, 200,000 users take 3-4 days, and 500,000+ users take over a week at continuous throughput.

More from our Blog

Ada to Ada Migration: The CTO's Technical Guide
Migration Guide

Ada to Ada Migration: The CTO's Technical Guide

A technical guide to migrating between Ada instances — covering API constraints, knowledge transfer, conversation archival, end-user handling, and the edge cases that break DIY scripts.

Nachi Nachi · · 29 min read