Teamtailor to Ashby Migration: The CTO's Technical Guide
Technical guide to migrating from Teamtailor to Ashby. Covers data model mapping, API rate limits, the HTTP 200 error trap, and step-by-step migration scripts.
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 Teamtailor to Ashby is a schema-translation and API-orchestration problem, not a CSV drag-and-drop. Teamtailor's data model is career-site-first — built on a JSON:API relational structure where a single candidate record fans out into job applications, custom field values, notes, uploads, and activities, each requiring separate API calls to extract. Ashby is an analytics-first ATS and CRM with a fundamentally different API architecture: an RPC-style interface where even read operations require POST requests, errors hide inside HTTP 200 responses, and custom field values must be set through a dedicated endpoint after record creation.
Ashby does not offer an automated API migration connector for Teamtailor. Their automated connectors currently support Greenhouse, Workday, and some Lever migrations. Ashby does list Teamtailor on its file-based historical import route, but that path has significant limitations — feedback comes in as notes, and it does not import job postings, interview plans, or openings (docs.ashbyhq.com). That leaves most teams choosing between a lossy CSV import, the file-based historical import, a custom API-based ETL pipeline, or a managed migration service.
This guide covers the data model translation, API constraints on both sides, the specific engineering traps that break DIY scripts, and what a clean migration actually requires.
For related context, see our guide on common ATS migration gotchas and the Greenhouse to Ashby migration for a comparison of Ashby's API behavior against a different source platform.
Why Scaling Teams Move from Teamtailor to Ashby
Companies typically outgrow Teamtailor when their talent acquisition maturity shifts from inbound marketing to outbound sourcing and complex, multi-stage evaluations.
Teamtailor is a recruitment marketing platform designed around employer branding. Its strengths are its career site builder, Connect (talent CRM), and candidate experience analytics. It works well for companies where recruitment marketing is the priority.
Ashby is an all-in-one ATS + CRM + analytics + scheduling platform built for data-driven recruiting operations. Companies typically migrate for three reasons:
- Consolidated tooling. Ashby replaces the ATS + scheduling tool + analytics dashboard stack with a single product. Teams running Teamtailor alongside separate interview scheduling and BI tools see immediate workflow simplification.
- Structured analytics. Ashby's reporting engine is deeply integrated — pipeline velocity, pass-through rates, and interviewer calibration are native, not bolted on.
- Scalable interview operations. Ashby's structured interview plans with feedback forms and scorecards enforce evaluation consistency in ways Teamtailor's more flexible interview kit approach does not.
The trade-off is real: Teamtailor's career site and employer branding tools are stronger out of the box. Teams migrating should plan to rebuild their career page experience in Ashby or use Ashby's API to power a custom careers page.
The technical driver for the migration is usually the need for a strict relational schema. Ashby separates the Candidate (the person), the Job (the open headcount), and the Application (the link between the person and the job at a specific point in time). This architecture enables advanced reporting but requires a precise data migration strategy.
Teamtailor vs. Ashby: The Data Model Gap
Both platforms share a Candidate → Application → Job hierarchy, but the implementation details diverge significantly.
Teamtailor's relational JSON:API model
Teamtailor operates with an open-ended relational model. The candidate object holds basic profile data (name, email, phone, resume). Everything else — custom field values, question answers, notes, uploads, activities, partner results — exists as separate related resources linked via JSON:API relationships.
Extracting a single complete candidate record from Teamtailor requires multiple API calls: one for the candidate, one for their job applications, one for custom field values, one for notes, one for uploads, and so on. Teamtailor explicitly splits between candidate-level data (CV, custom fields, locations, comments outside job context) and job-level data (job-context comments, candidate-uploaded job documents, questions and answers, interviews, partner assessments, NPS responses) (support.teamtailor.com).
Ashby's RPC-style model
Ashby organizes data into Candidates, Applications (also referred to as "job considerations" in Ashby's UI), Jobs, Interview Stages, Interview Feedback, Offers, and Notes. Applications link candidates to jobs and carry their own custom fields. Interview feedback is structured and tied to specific interview stages within an application.
The write path is equally fragmented: creating a candidate with full data requires a candidate.create call, followed by application.create to link them to a job, then customFields.setValue for each custom field, and candidate.createNote for historical notes. You cannot set custom field values during candidate or application creation — each is a separate API call.
If you export a candidate from Teamtailor and push them into Ashby without reconstructing these relationships, you create orphaned Candidate records with no Application history, rendering your historical pipeline analytics useless.
Data Mapping Table
| Teamtailor Object | Ashby Equivalent | Notes |
|---|---|---|
| Candidate | Candidate | Core profile fields map 1:1. Keep source_candidate_id for audit. |
| Job | Job | Title, team, location. Rebuild interview plans manually. |
| Job Application | Application | Link candidate to job. Stage mapping requires translation. |
| Stage | Interview Stage | Teamtailor stages are flexible per-job; Ashby stages are tied to interview plans. |
| Custom Field Value | Custom Field (via customFields.setValue) |
Must pre-create matching fields in Ashby. Separate API call per field. |
| Note / Comment (candidate-level) | Candidate Note | Free-text migration. Preserve timestamps via createdAt. |
| Note / Comment (job-level) | Candidate Note or Application history | Ashby's historical import converts job-level feedback to notes. Decide your target model up front. |
| Activity | No direct equivalent | Flatten to notes or discard. Ashby auto-generates its own activity log. |
| Upload / Attachment | Candidate File | Download from Teamtailor, re-upload via Ashby's file.createFileUploadHandle endpoint. |
| Answer (to question) | Custom Field or Note | Map structured answers to custom fields; free-text to notes. |
| Scorecard / Interview Kit Feedback | Interview Feedback | Lossy — Teamtailor's skill/trait scoring doesn't map 1:1 to Ashby's feedback forms. |
| Department | Department | Recreate in Ashby admin before import. |
| Location | Location | Recreate in Ashby admin before import. |
| Tag | Candidate Tag | Create tags in Ashby first, then apply via API. |
Teamtailor activities (stage movements, emails sent, status changes) have no direct write endpoint in Ashby's API. This data is either flattened into candidate notes or lost entirely. Plan for this gap before migration.
Migration Methods: CSV, File-Based, API, and Managed
1. Native CSV Export → Ashby Bulk Import
How it works: Export candidates from Teamtailor's candidate bank as a CSV file (Bulk select → Export). Import into Ashby via Admin → Data Management → Bulk Data Import (support.teamtailor.com).
What you get: Basic candidate profiles — name, email, phone, LinkedIn URL. Ashby's CSV import also supports creating openings in bulk.
What you lose: Interview history, stage progression timestamps, custom field values, notes, attachments, scorecard feedback, question answers, and application-to-job relationships. Ashby's documentation states that self-serve bulk CSV import "only supports importing candidate data" and "is not a comprehensive view of the candidates' lifecycle."
When to use it: Small teams (<500 candidates) that don't need historical hiring data and just want contact records migrated to start sourcing.
Complexity: Low.
2. Ashby's File-Based Historical Import
How it works: Export Teamtailor data, provide the files to Ashby, prebuild open jobs in Ashby, and Ashby maps candidates and applications into the system (docs.ashbyhq.com).
What you get: Candidates, applications (as job considerations), optional job mapping, and feedback imported as notes.
What you lose: Job postings, interview plans, and openings are not created by this process. Feedback comes in as notes, not native interview feedback objects. If your recruiters depend on strict job-context separation for comments or feedback, that structure is flattened (docs.ashbyhq.com).
When to use it: You want more historical coverage than CSV without building a custom ETL pipeline, and you can accept the feedback-as-notes trade-off.
Complexity: Medium.
3. Custom API-Based ETL Pipeline
How it works: Extract data from Teamtailor's JSON:API endpoints using an Admin-scoped API key. Transform records to match Ashby's schema. Load into Ashby via their RPC-style API.
What you get: Highest-fidelity migration — candidates, applications, job linkages, custom fields, notes, attachments, and (partially) interview feedback.
What you lose: Teamtailor activities that have no Ashby write endpoint. Scorecard fidelity where Teamtailor's skill/trait model doesn't map to Ashby's feedback form structure.
When to use it: You have a dedicated engineer for 2–4 weeks, need historical candidate lifecycle data preserved, and want full control over data mapping.
Complexity: High.
4. Middleware (Zapier, Make)
How it works: Connect Ashby and Teamtailor with prebuilt triggers and actions. Zapier currently exposes Ashby actions such as Create Candidate, Create Candidate Note, and Create Opening. Make offers an Ashby connector as a community integration (zapier.com).
What you get: Light post-migration automations or small delta syncs.
What you lose: Cannot handle historical bulk loads. Will trigger API rate limits on both sides quickly. Fails on complex multi-object relationships (attaching historical feedback to a specific past application). Make's Ashby connector is community-maintained, not officially supported.
When to use it: Post-cutover automation or small operational syncs — not historical backfills.
Complexity: Medium.
5. Managed Migration Service
How it works: A migration partner handles extraction, transformation, loading, and validation as one execution plan.
When to use it: Your engineering team is shipping product, you have a hard cutover deadline, or your data includes complex custom fields and multi-year hiring history.
Complexity: Low for your team.
Migration Method Comparison
| Criteria | CSV Import | File-Based Import | Custom ETL | Middleware | Managed Service |
|---|---|---|---|---|---|
| Candidate profiles | ✅ | ✅ | ✅ | ✅ | ✅ |
| Applications + job links | ❌ | ✅ | ✅ | ❌ | ✅ |
| Custom fields | ❌ | Partial | ✅ | ❌ | ✅ |
| Notes & comments | ❌ | ✅ (as notes) | ✅ | Limited | ✅ |
| Interview feedback | ❌ | As notes only | Partial | ❌ | Partial |
| Stage history | ❌ | Partial | Partial | ❌ | Partial |
| Engineering effort | None | Low | 2–4 weeks | Low | None |
| Risk of silent data loss | Low (data just missing) | Low | High (API traps) | Medium | Low (validated) |
Ashby API Quirks and Rate Limits
This section covers the specific engineering traps that break most DIY migration attempts. If you're evaluating build-vs-buy, these constraints determine the real engineering cost.
RPC-style POST-for-everything architecture
Ashby's API does not follow REST conventions. Every endpoint — including list and info lookups — requires a POST request with a JSON body. If your HTTP client or middleware assumes GET for reads, every call will fail.
# Listing candidates in Ashby — this is a POST, not a GET
curl -X POST https://api.ashbyhq.com/candidate.list \
-u YOUR_API_KEY: \
-H "Content-Type: application/json" \
-d '{"limit": 100}'The HTTP 200 OK error trap
This is the most dangerous behavior for migration scripts. Ashby returns HTTP 200 with success: false in the response body for what would normally be 4XX errors. If your script checks only HTTP status codes, it will record failed writes as successes.
{
"success": false,
"errorInfo": {
"code": "application_not_found",
"message": "Application not found - are you lacking permissions to edit candidates?"
}
}Every response from Ashby must be parsed and the success field explicitly checked. Standard HTTP client retry logic, monitoring dashboards, and API gateway metrics will all report these failures as successes unless you add application-level validation. For a deeper look at why AI-generated scripts are especially vulnerable to this pattern, see why DIY AI migration scripts fail.
Ashby returns HTTP 200 for failed requests. Your migration script MUST parse the response body and check the success field on every single API call. Standard HTTP status code checks will miss errors entirely.
Ashby rate limits
Ashby enforces a rate limit of 1,000 requests per minute per API key. For report endpoints, the limit drops to 15 requests per minute per organization.
This sounds generous until you do the math: migrating a single candidate with an application, three custom fields, two notes, and an attachment requires at minimum 8 API calls. At 10,000 candidates, that's 80,000+ requests — roughly 80 minutes at maximum throughput with zero retries.
Ashby list endpoints use opaque cursors and sync tokens. You page through results by passing nextCursor values until moreDataAvailable is false. Sync tokens can expire — if you receive a sync_token_expired error mid-migration, you must restart a full sync. Build checkpointing into your script.
Teamtailor rate limits
Teamtailor's API allows a maximum of 50 requests per 10 seconds. Their documentation recommends adding a 300ms delay between each request to stay safely below the threshold. Teamtailor exposes rate limit state via X-Rate-Limit-Limit, X-Rate-Limit-Remaining, and X-Rate-Limit-Reset headers. Exceeding the limit returns HTTP 429.
Teamtailor pagination caps at 30 records per page (page [size] max is 30). Extracting 10,000 candidates requires at minimum 334 paginated requests — just for the candidate list, before you fetch related resources.
Do not attempt concurrent multi-threaded extraction from Teamtailor without strict token bucket rate limiting. A naive concurrent script will trigger 429s and potentially result in a temporary IP ban.
The candidate.search cap
Ashby's candidate.search endpoint is capped at 100 results (developers.ashbyhq.com). Do not use broad search calls as your primary deduplication strategy during migration. Maintain an explicit crosswalk table of Teamtailor IDs → Ashby IDs using exact email matching. Fuzzy lookups break at scale.
Custom fields require separate write calls
In Ashby, you cannot set custom field values during candidate or application creation. You must first create the record, then call customFields.setValue for each field (developers.ashbyhq.com). This doubles the API call count for any record with custom data and introduces a dependency chain — if the create succeeds but the custom field write fails, you have an incomplete record that needs retry logic.
Step-by-Step Migration Architecture
Phase 1: Extract from Teamtailor
Use an Admin-scoped API key (read permissions) to extract all resources via Teamtailor's JSON:API endpoints. Extraction order matters: pull reference data first (departments, locations, stages, custom field definitions), then jobs, then candidates, then job applications and related resources.
import requests
import time
BASE_URL = "https://api.teamtailor.com/v1"
HEADERS = {
"Authorization": "Token token=YOUR_TT_API_KEY",
"X-Api-Version": "20161108",
"Content-Type": "application/vnd.api+json"
}
def fetch_all(resource):
"""Paginate through a Teamtailor resource, respecting rate limits."""
url = f"{BASE_URL}/{resource}?page[size]=30"
all_records = []
while url:
response = requests.get(url, headers=HEADERS)
if response.status_code == 429:
reset = int(response.headers.get("X-Rate-Limit-Reset", 10))
time.sleep(reset)
continue
data = response.json()
all_records.extend(data.get("data", []))
url = data.get("links", {}).get("next")
time.sleep(0.3) # 300ms delay per Teamtailor recommendation
return all_records
candidates = fetch_all("candidates")
jobs = fetch_all("jobs")
applications = fetch_all("job-applications")Phase 2: Transform
The transformation layer handles:
- Field mapping: Teamtailor's
first-name+last-name→ Ashby's singlenamefield - Custom field translation: Match Teamtailor custom field IDs to pre-created Ashby custom field IDs
- Relationship reconstruction: Build a lookup table of Teamtailor job IDs → Ashby job IDs (after jobs are created)
- Data cleanup: Deduplicate candidates on email, normalize email formats, strip invalid phone numbers
- Attachment handling: Download resume files from Teamtailor URLs and prepare for Ashby upload
- Source ID preservation: Store Teamtailor record IDs in your crosswalk table for post-migration debugging
Phase 3: Load into Ashby
import requests
import base64
import time
ASHBY_BASE = "https://api.ashbyhq.com"
API_KEY = "YOUR_ASHBY_API_KEY"
AUTH = base64.b64encode(f"{API_KEY}:".encode()).decode()
ASHBY_HEADERS = {
"Authorization": f"Basic {AUTH}",
"Content-Type": "application/json"
}
def ashby_post(endpoint, payload):
"""POST to Ashby with success:false checking and rate limit handling."""
response = requests.post(
f"{ASHBY_BASE}/{endpoint}",
json=payload,
headers=ASHBY_HEADERS
)
body = response.json()
# CRITICAL: Ashby returns 200 even on errors
if not body.get("success"):
error = body.get("errorInfo", body.get("errors", "Unknown error"))
raise Exception(f"Ashby API error on {endpoint}: {error}")
return body.get("results")
def migrate_candidate(tt_candidate, field_map, job_id_map):
"""Migrate a single candidate with applications, custom fields, and notes."""
# Step 1: Create candidate
candidate = ashby_post("candidate.create", {
"name": f"{tt_candidate['first_name']} {tt_candidate['last_name']}",
"email": tt_candidate.get("email"),
"phoneNumber": tt_candidate.get("phone"),
"linkedInUrl": tt_candidate.get("linkedin_url")
})
ashby_candidate_id = candidate["id"]
# Step 2: Create applications (link to jobs)
for app in tt_candidate.get("applications", []):
ashby_job_id = job_id_map.get(app["tt_job_id"])
if ashby_job_id:
ashby_post("application.create", {
"candidateId": ashby_candidate_id,
"jobId": ashby_job_id,
"createdAt": app.get("created_at")
})
# Step 3: Set custom fields (separate call per field)
for field in tt_candidate.get("custom_fields", []):
ashby_field_id = field_map.get(field["tt_field_id"])
if ashby_field_id:
ashby_post("customFields.setValue", {
"objectType": "Candidate",
"objectId": ashby_candidate_id,
"fieldId": ashby_field_id,
"value": field["value"]
})
# Step 4: Migrate notes
for note in tt_candidate.get("notes", []):
ashby_post("candidate.createNote", {
"candidateId": ashby_candidate_id,
"note": note["content"],
"createdAt": note["created_at"]
})
time.sleep(0.06) # Stay under 1000 req/min
return ashby_candidate_idLoad order is strict: Departments → Locations → Jobs → Candidates → Applications → Custom Fields → Notes → Attachments. Each step depends on IDs generated by the previous step. You cannot attach an application to a job that does not exist.
Common Pitfalls and Edge Cases
Duplicate candidates. Teamtailor allows the same person to exist once with multiple job applications. Ashby deduplicates on email. If your Teamtailor data has candidates with typos in email addresses, you'll create duplicates in Ashby that require manual cleanup. Use immutable source IDs plus exact email matching — do not rely on Ashby's candidate.search for bulk reconciliation since it caps at 100 results.
Resume file handling. Teamtailor stores resumes and returns URLs via the uploads resource. These URLs may be temporary. Download all files during extraction and re-upload to Ashby using the file.createFileUploadHandle endpoint. Randomly sample migrated candidate profiles post-migration to ensure PDFs open correctly and were not corrupted during transfer.
Stage mapping ambiguity. Teamtailor stages are flexible per-job (Inbox, In review, Interview, Offer, Hired, Rejected — all customizable). Ashby's interview stages are tied to structured interview plans. Applications land in the current stage, not a historical one. Ashby does expose application.updateHistory for some history reconstruction (developers.ashbyhq.com), but plan for partial fidelity on stage progression data.
Missing activity write endpoints. Teamtailor tracks granular activity data (who moved a candidate, when, what emails were sent). Ashby generates its own activity log and does not expose a write endpoint for historical activities. This data must be flattened into notes or accepted as a loss.
Custom field type mismatches. Teamtailor supports text, number, date, boolean, URL, email, phone, single select, and multi-select custom fields. Ashby's custom fields have their own type system. Multi-select values may need to be comma-joined into text fields if Ashby doesn't support the exact type. Normalize picklists before load; archive unused values instead of inventing them mid-run. Ashby's public custom-field API is scoped to Candidate, Application, Job, and Opening objects. For more on this, see ATS migration gotchas.
Deactivated user mapping. If a recruiter left the company and their email is deactivated, Ashby will not let you assign them as the owner of a historical interview. Map deactivated Teamtailor users to a generic "Legacy System User" in Ashby, or prepend their name to the text of the historical feedback.
Pseudo-objects in Teamtailor. If you've been using Teamtailor as a lightweight recruiting CRM with agencies, campuses, or client accounts stored in tags and custom fields, treat that as design work, not import work. Those entities usually end up collapsed into Ashby's core objects, tags, notes, or an external warehouse.
Pre-Migration Planning Checklist
Before writing a single line of migration code:
- Audit Teamtailor data volume. Count candidates, jobs, applications, custom fields, notes, and attachments. This determines API call count and migration duration.
- Export custom field definitions. Document every Teamtailor custom field (name, type, options for select fields). Pre-create matching fields in Ashby. Ashby enforces strict data typing.
- Map pipeline stages. Document each Teamtailor job's stage configuration. Decide which Ashby interview plan each maps to. Do not assume stage names line up.
- Identify disposable data. Rejected candidates older than your retention policy? Draft jobs never published? Exclude them to reduce migration scope.
- Create Ashby reference data first. Departments, locations, job templates, interview plans, and custom field definitions must exist before any candidate data is loaded. Ashby's file-based historical import will not create job postings, interview plans, or openings for you (docs.ashbyhq.com).
- Generate API keys with correct permissions. Teamtailor: Admin-scoped, read-only. Ashby:
candidatesWrite,hiringProcessMetadataWrite, andorganizationReadat minimum. - Plan GDPR handling. Candidate consent status in Teamtailor must be respected during migration. If a candidate's consent has expired, do not transfer their data. See our GDPR-compliant data migration blueprint for detailed compliance guidance.
For a comprehensive pre-migration framework, see our ATS data migration checklist.
Validation and Post-Migration Testing
Migration without validation is data loss you haven't discovered yet.
Record count parity
Compare counts at every level:
| Object | Teamtailor Count | Ashby Count | Delta |
|---|---|---|---|
| Candidates | X | X | Should be 0 |
| Active applications | X | X | Should be 0 |
| Jobs | X | X | Should be 0 |
| Notes | X | X | Should be 0 |
| Custom field values | X | X | Should be 0 |
Field-level sampling
Randomly sample 5–10% of migrated candidates. For each, verify:
- Name, email, phone match source
- Correct job application linkage
- Custom field values present and correctly typed
- Notes content and timestamps preserved
- Attachments accessible and uncorrupted
UAT with recruiters
Have your recruiting team open 10 known candidate records in Ashby and verify the data matches their Teamtailor recollection. Recruiters catch context errors (wrong stage, missing interview notes) that automated checks miss.
Rollback plan
Ashby does not provide a bulk delete or rollback API. If a migration goes wrong, cleanup is manual. Run a test migration into a separate Ashby workspace before touching production. Ashby does not offer a public sandbox — use a demo workspace for testing.
Post-Migration: What to Rebuild in Ashby
Data migration gets your records across. These items require manual rebuild:
- Interview plans and stage configurations. Ashby's structured interview framework must be configured per-job. This is process design, not data migration.
- Email templates and automation triggers. Teamtailor's triggers (move-to-stage actions, automated emails) don't transfer. Rebuild in Ashby's workflow system.
- Career site. If you used Teamtailor's career site builder, plan to rebuild on Ashby's hosted career page or use their API for a custom page.
- Integrations. Reconnect scheduling tools, HRIS systems, assessment partners, and sourcing extensions.
- User permissions and roles. Recreate team structures and access controls in Ashby's admin panel.
- Reporting views. Configure Ashby's analytics dashboards for your team's KPIs.
- Training. Ashby's UI paradigm is different from Teamtailor. Budget time for recruiter onboarding — especially around structured feedback forms, the candidate vs. job-consideration views, and the analytics dashboard.
DIY Scripts vs. Managed Migration: The Real Trade-Off
Build in-house if:
- You have a dedicated engineer available for 2–4 weeks (including testing)
- Your data model is simple (few custom fields, no scorecard data, <5,000 candidates)
- You can tolerate some data loss on activities and stage history
Use a managed service if:
- Your engineering team is shipping product features and cannot absorb 2–4 weeks of migration work
- You have complex custom fields, multi-year interview history, or >10,000 candidates
- You need checksum-verified record parity and a written validation report
- Your cutover date is fixed and there's no room for debugging API edge cases
The hidden cost of DIY is not the initial build — it's the three rounds of debugging when Ashby's success: false responses silently drop 3% of your records. For context on how migration work displaces product velocity, see how in-house data migrations silently kill product velocity.
At ClonePartner, we've handled ATS migrations where Ashby's non-standard error handling and Teamtailor's per-request rate limiting consumed the majority of engineering effort. Our scripts natively parse Ashby's success: false responses, manage Teamtailor's X-Rate-Limit headers with adaptive throttling, and deliver a validation report with record-count parity at every object level.
Best Practices for a Clean Migration
- Back up everything before you start. Export a full CSV from Teamtailor as a baseline reference, even if you're doing an API migration.
- Run a test migration first. Load 50–100 candidates into a test Ashby workspace. Validate every object type before running the full migration.
- Log every API call and response. When (not if) something fails, you need a complete audit trail to identify which records were affected.
- Validate incrementally. Don't wait until the end to check counts. Validate after each object type is loaded.
- Freeze Teamtailor during cutover. Coordinate with your recruiting team to stop creating new candidates in Teamtailor 24 hours before the final migration run. Records created during migration will be missed.
- Preserve source IDs. Store Teamtailor record IDs as a custom field or tag in Ashby. This makes post-migration debugging and audit dramatically easier.
- Keep Teamtailor readable after cutover. Don't decommission Teamtailor immediately. Maintain read access until your team has fully validated the migration and signed off. Rollback means switching the system of engagement back, not reconstructing history from memory.
Frequently Asked Questions
- Can I migrate from Teamtailor to Ashby using CSV files?
- Yes, but with severe limitations. Ashby's self-serve CSV import only supports basic candidate profiles — name, email, phone, LinkedIn. You lose all interview history, custom field values, notes, attachments, pipeline stage data, and application-to-job relationships. It's suitable for small teams that don't need historical hiring data.
- Does Ashby support a native migration from Teamtailor?
- Ashby's automated API migration connectors support Greenhouse, Workday, and some Lever migrations — not Teamtailor. Ashby does list Teamtailor on its file-based historical import route, which can import candidates, applications, optional jobs, and feedback as notes, but it does not import job postings, interview plans, or openings.
- What are the API rate limits for Ashby and Teamtailor?
- Ashby enforces 1,000 requests per minute per API key (15/min for report endpoints). Teamtailor allows 50 requests per 10 seconds and recommends a 300ms delay between requests. Since migrating one candidate with applications, custom fields, and notes requires 8+ API calls, a 10,000-candidate migration takes roughly 80+ minutes at maximum throughput.
- Why does Ashby return HTTP 200 for API errors?
- Ashby's API uses an RPC-style architecture where application-level errors return HTTP 200 with a 'success: false' flag and an errorInfo object in the response body. Standard HTTP status code checks will miss these errors entirely. Migration scripts must parse the response body and check the success field on every call.
- How long does a Teamtailor to Ashby migration take?
- A CSV-only migration can be done in a day but loses most data. Ashby's file-based historical import takes days with Ashby's involvement. A custom API-based migration takes 2–4 weeks of engineering effort including scripting, testing, and validation. A managed migration service typically completes the full transfer in days, including validation.