ATS & HRMS migration guide

The Definitive Guide to Migrating to Bullhorn

Bullhorn is a staffing-industry ATS whose import model rewards teams that pre-create custom fields, respect the Custom Import 100-record pagination quirk, and load entities in the strict Candidate → JobOrder → JobSubmission → Placement order.

22 min read 9 sections Updated May 27, 2026
Bullhorn ATS & CRM
Employees
Compensation
Benefits
Time Off
Org Structure
Performance

Inside this guide

What you'll learn, section by section

  1. 01

    Why teams migrate to Bullhorn

    The four shapes a Bullhorn migration takes, and what makes the platform easier — or harder — than the category average.

  2. 02

    The Bullhorn data model you need to map into

    Entities, custom fields, associations, and the externalID upsert key you'll wire on every load — the destination schema decoded.

  3. 03

    Pre-migration prep — the work before you touch Bullhorn

    What must be true on the source, the destination, and across the team before the first row hits the Data Loader.

  4. 04

    Import mechanisms: UI wizard and Data Loader

    Two paths in, each with different limits and shapes. Picking the wrong one is how staffing-agency migrations stall at scale.

  5. 05

    Mapping your data into Bullhorn

    The longest section — because field mapping is where almost every migration that fails actually breaks.

  6. 06

    The pitfalls that derail Bullhorn migrations

    Specific failure modes — ranked by impact, each tied to the exact Bullhorn mechanism that breaks.

  7. 07

    Validation and cutover

    What to verify after the import job, in what order — and how to fail safely when something is wrong.

  8. 08

    Migration partners and tools

    Bullhorn Marketplace partners, ETL vendors, specialist migration shops — what each is good for and how to choose.

  9. 09

    Frequently asked questions

    The questions every Bullhorn migration team works through before they sign the scope.

Section 01

Why teams migrate to Bullhorn

The four shapes a Bullhorn migration takes, and what makes the platform easier — or harder — than the category average.

Bullhorn, Inc. is an American cloud company headquartered in Boston, with operations in St. Louis, London, Brighton, Sydney and Rotterdam 1. The platform provides applicant tracking, CRM and operations software for the staffing industry, and more than 10,000 firms run their recruitment workflows on it 12.

The typical Bullhorn customer is a staffing or recruitment agency — five to several hundred recruiters — managing candidates, client contacts, job orders and placements in one connected workflow. Compared with generalist ATS products like Greenhouse, Bullhorn positions on staffing-industry depth: Sendouts, Tearsheets, VMS Sync and a marketplace of 300+ pre-integrated partners 12. Compared with JobAdder or Crelate, it positions on platform maturity and a richer toolset for staffing workflows.

The shapes of migration that actually land on Bullhorn tend to fall into four patterns. First, homegrown-ATS replacements: an agency moving off a spreadsheet, an Access database or a bespoke in-house tool with limited outreach capabilities 11. Second, vertical-ATS swaps, where the source is Crelate, JobAdder, PCRecruiter or Avionté and object parity is reasonable but custom-Category structures are not.

Third, generalist-ATS exits — Greenhouse, Workable, Lever — where the source pipeline-per-job model has to be re-shaped into Bullhorn's Candidate → JobSubmission → Placement chain. Fourth, M&A consolidation, where an acquired agency runs on a different ATS. A Crelate migration usually has clean entity parity but messy custom-Category labels; a Greenhouse migration has rich scorecards that do not move natively.

What makes migrating *to* Bullhorn easier than the category average is the Bullhorn Data Loader — an open-source Java tool maintained by Bullhorn that ingests CSV for nearly every entity and upserts by externalID 3.

What makes it harder than the average is the entity ordering constraint — Candidates must exist before JobSubmissions, JobOrders before JobSubmissions, ClientContacts before Notes — and the Custom Import quirk where clicking *Next* more than once on the preview screen silently duplicates records 142. Resume Parser settings, EEOC fields and JobSubmission status histories all need explicit decisions up front.

Reports built in Bullhorn Reporting (formerly Canvas), Pulse activity rules and Bullhorn Automation journeys do not import — they are rebuilt from documentation. Teams that scope for that work finish on time; teams that assume parity do not.

Teams that scope for the rebuild work up front finish on time; teams that assume parity do not.

Section 02

The Bullhorn data model you need to map into

Entities, custom fields, associations, and the externalID upsert key you'll wire on every load — the destination schema decoded.

Bullhorn platform Contacts Companies Deals Tickets Tasks Notes
Standard objects orbit the platform; every association can be many-to-many with optional labels.

Bullhorn's ATS is built around a small set of staffing-specific entities, each with default and custom fields, and associations that connect them.

Before you can map a field on the source side, you need to know which destination entity the row belongs on, which fields it requires, and which value will serve as its upsert key. The table below summarises the entities you will touch.

Object Stores Required on import Tier
Candidate People you place — applicants, contractors, perm hires firstName, lastName, status All editions
ClientContact Hiring-manager-side individuals at client companies firstName, lastName, clientCorporation, status All editions
ClientCorporation Client companies you place candidates with name, status All editions
Lead / Opportunity Pre-conversion sales records (CRM side) name, status, owner Bullhorn CRM / Front Office tiers
JobOrder Open requisitions from clients title, clientCorporation, clientContact, status All editions
JobSubmission A candidate's application against a JobOrder candidate, jobOrder, status, dateWebResponse All editions
Sendout Candidate package sent to a client contact candidate, jobOrder, clientContact, dateSent All editions
Placement Confirmed hire / contract assignment candidate, jobOrder, dateBegin, employmentType All editions
Note, Task, Appointment Engagement and activity history action / subject, dateAdded, commentingPerson All editions
Custom Objects (1–10 per parent) Customer-defined sub-records attached to core entities Schema defined by Bullhorn Support first Front Office Growth/Enterprise: 10 per entity with 55 fields each; Bullhorn ATS: 2 per entity; ATS Growth: none [16]

Bullhorn's upsert key is externalID — a single-line text field on every core entity that you set yourself and use to match incoming rows against existing records 3. Several entities also expose customTextX fields that can be promoted to external-ID-style match keys via the Data Loader ExistField setting 3.

Plan for externalID on day one: stamp the source primary key (or a UUID) into every row before export, and use it on every re-run so updates are deterministic. Without externalID, the Custom Import wizard matches on a name/email heuristic unsafe for production 142.

Custom field edit types determine validation and storage; the catalogue below covers what you can model and the limits that apply 17.

Field type Limits Notes
Custom Text 100 chars (Text) or longer for Text Block / DHTML Editor Can be changed to most other types except Float, Money, Date/Time, Integer 17
Custom Float / Numeric / Money / Percentage Numeric storage; FLOAT / NUMERIC / MONEY specialisations Float can convert only to Integer, Money, Mini Picker or Drop-Down 17
Custom Integer 32-bit integer Can only be changed to Mini Picker or Drop-Down 17
Custom Date / Date-Time Stored in Eastern Time on Bullhorn servers Time portion is often ignored on Date-only fields; convert source datetimes carefully 27
Drop-Down / Mini Picker (picklist) Defined per field in Field Mappings Internal value vs display label distinction matters
Check Box Boolean Only Text Block and DHTML Editor fields can be retyped to Check Box 17
Custom Objects per parent entity 10 / 2 / 0 per tier; 55 fields each Front Office Enterprise / Bullhorn ATS / ATS Growth respectively 16
Categories (industry, skill, specialty) Tag-style attached to Candidate, ClientContact, JobOrder, Placement Multi-value; mapped through Business Sector and Specialty parent lists

Relationships in Bullhorn are modelled as to-one and to-many associations between entities — JobSubmission.candidate is to-one, Candidate.notes is to-many. The Data Loader resolves them by externalID in cross-reference columns.

There is no native cascade-delete: deleting a parent does not delete child Notes, JobSubmissions or Appointments, a behaviour to reproduce via Bullhorn Automation or scripted cleanup if your source relied on it.

Section 03

Pre-migration prep — the work before you touch Bullhorn

What must be true on the source, the destination, and across the team before the first row hits the Data Loader.

The single best predictor of a clean Bullhorn migration is how much work you do on the source side before the first Data Loader command runs. Bullhorn's own guidance on data hygiene notes that stale candidate records, duplicate emails and outdated Categories are the main reason post-migration search results drift from what recruiters expect 29.

The single best predictor of a clean migration is how much work you do before the first Data Loader command runs.

Treat the source export as raw material that needs shaping to Bullhorn's expected formats — email lowercased, phone normalised, picklist values rewritten to destination drop-down values, dates as MM/DD/YYYY or ISO-8601, and owners resolved to Bullhorn usernames.

Source-side prep

  • Audit and dedup the source candidate database before export — email-case-only duplicates, role-based addresses and obviously stale records (last-touched 5+ years ago, never placed) should be pruned now, not after they hit Bullhorn.
  • Stamp externalID on every source record — Candidate, ClientContact, ClientCorporation, JobOrder, JobSubmission and Placement. Use the source primary key or a UUID. This is the single most-important prep task because every re-run, reconciliation query and Automation lookup depends on it 3.
  • Normalise picklist values for Candidate status, JobOrder status, JobSubmission status, Placement employmentType and every custom drop-down — Bullhorn matches on the Field Mappings value, not the display label, and silently drops rows targeting undefined values 62.
  • Decide resume-file scope. Each candidate has zero, one or many resumes. Decide latest-only, all-versions, or none — the resume leg is its own pipeline that runs *after* Candidate rows land.
  • Convert dates to Eastern Time. Bullhorn stores all datetimes in ET on the server regardless of user timezone — a date loaded with no offset is interpreted as ET midnight, silently shifting dates by up to 24 hours for non-US-east sources 2771.

Destination-side prep

  • Provision a sandbox — Bullhorn supports Developer, Partial and Full-Copy Sandboxes refreshed daily, weekly and monthly, with Partial capped at 5 GB and 10,000 records per object 92156. Use Partial or Full-Copy for the dry-run.
  • Provision Corporate Users first, ideally via SSO, and assign User Types and Departments before importing — owner assignment falls back to the load user if a referenced owner does not exist 13133.
  • Pre-create every custom field in Admin → Field Mappings on the right entity with the right edit type — Custom Float, Integer, Text and Date have asymmetric retype rules that catch teams later 17.
  • Pre-create every Custom Object via a Bullhorn Support ticket — Custom Objects cannot be created through the UI directly 16160, and they must exist before child rows can be loaded.
  • Set Resume Parser settings. Enable email-based resume parsing via support ticket if backfilling through the parser, and decide which parsed fields overwrite vs. fill blanks 106.

People prep

Cutover only works if humans cooperate. Lock down a source-system freeze window — typically 24 to 72 hours for teams whose recruiters live in the system. Train recruiters on Candidate search, JobSubmission status transitions and the Notes tab before go-live. A typical small-agency migration runs two to six weeks of elapsed time 104; data depth, custom Categories and resume volume drive the upper end.

Section 04

Import mechanisms: UI wizard and Data Loader

Two paths in, each with different limits and shapes. Picking the wrong one is how staffing-agency migrations stall at scale.

Bullhorn exposes two main load paths and the right one depends on dataset size, entity mix, and whether you need to re-run idempotently. The Custom Import wizard covers small Candidate/Contact/Lead loads. The Bullhorn Data Loader handles repeatable, large CSV loads across all entities.

Custom Import wizard

The native Custom Import lives at Menu → Tools → Custom Import 142. It accepts CSV only — Excel files must be saved as CSV first — and supports just three entities: Candidate, Contact and Lead 32. The flow: pick Record Type, upload CSV, accept the auto-mapping, name the template, click *Perform Import*, review the first 100 records, click *Next* exactly once, and wait.

The single most-important warning in the Bullhorn docs: clicking *Next* more than once on the preview screen creates duplicate records 142. Teams that lose patience because the page appears unresponsive find every candidate doubled. Recovery: mass-delete the newly-added rows (they default to *Imported* status) and re-import the cleaned file 133.

Bullhorn Data Loader

The Bullhorn Data Loader is an open-source Java tool published at github.com/bullhorn/dataloader and maintained by Bullhorn 314. It runs from the command line against a directory of CSV files whose names start with the target entity — Candidate.csv, JobOrder.csv — and supports load, update, delete and convertAttachments commands.

Configuration lives in dataloader.properties, where the ExistField setting declares the upsert key per entity (typically externalID, but any unique field works) 3. With ExistField set, Data Loader does upsert; with it commented out or absent, every row is a fresh insert.

Data Loader handles cross-reference associations via column names like clientCorporation.externalID — set the source-side parent key in that column and Data Loader resolves it at load time. The same pattern works for Categories, Business Sector, Specialty and Custom Object parents.

Rule

Under 5,000 Candidates / Contacts / Leads on standard fields → Custom Import wizard. Any JobOrder, JobSubmission, Placement, Note, Appointment or large Candidate load → Bullhorn Data Loader.

Section 05

Mapping your data into Bullhorn

The longest section — because field mapping is where almost every migration that fails actually breaks.

SOURCE BULLHORN ATS & CRM FirstName, LastName firstname, lastname AccountName company AnnualRevenue annualrevenue Owner.Email hubspot_owner_id CreatedDate createdate
Field-mapping flow — every source field resolves to a destination property or an explicit drop.

Mapping is where every staffing-ATS migration earns its scars. The schema decisions in your mapping spreadsheet determine whether recruiters find candidates on day two, whether placement reports work on day five, and whether the team trusts the data by day thirty.

Work entity by entity in strict import order: ClientCorporations first (so JobOrders and ClientContacts can associate), then ClientContacts, Candidates, JobOrders, JobSubmissions, Sendouts, Placements, Notes / Tasks / Appointments, and resume files and Custom Objects last.

Candidates

Common source → Bullhorn Candidate mapping

Source Destination
  • candidate_id
    externalID (upsert key)

    Stamp source primary key here; set as ExistField in Data Loader 3

  • first_name / last_name
    firstName / lastName

    Required; preserve case as candidate-entered

  • email
    email

    Lowercase, trim, validate; not the primary upsert key

  • phone
    phone / mobile

    Bullhorn does not enforce E.164; normalise upstream for searchability

  • status / pipeline_stage
    status

    Map to a value defined in Field Mappings (e.g. Active, Placed, Inactive) — undefined values silently fail 62

  • owner / recruiter
    owner (CorporateUser association)

    Map by username or CorporateUser ID — unresolved owners default to Imported 133

  • skills / tags
    categories (Specialty / Skill tagging)

    Multi-value; use semicolon delimiter and pre-create parent values

  • source / referral_source
    source field + customText for legacy value

    Bullhorn's source list is restricted; collapse out-of-range values to Other

  • gender / ethnicity / veteran / disability
    EEOC fields on Candidate

    Sensitive; respect retention and consent policies before loading 130

ClientCorporations and ClientContacts

Common source → Bullhorn Client mapping

Source Destination
  • company_name
    ClientCorporation.name

    Required

  • company_id
    ClientCorporation.externalID

    Upsert key for cross-reference from JobOrder, ClientContact

  • industry
    businessSectorID

    To-one to the Business Sector picklist; pre-create values

  • contact email + name
    ClientContact fields with clientCorporation.externalID column

    Cross-reference resolves the parent company at load time

  • parent / subsidiary
    parentClientCorporation association

    Run as a second pass after all companies have IDs

JobOrders, JobSubmissions and Placements — the ATS deep dive

The job-requisition pipeline runs JobOrder → JobSubmission → Sendout → Placement, with status transitions logged as history on each entity. A JobOrder is the open requisition (e.g. *Senior RN, Boston*), a JobSubmission is a candidate's application against it, a Sendout is when that submission is packaged and sent to the client, and a Placement is the confirmed hire.

Recreate every JobOrder status (Open, Covered, Lost, Closed) and every JobSubmission status (New Lead, Internal Submission, Client Submission, Interview Scheduled, Offer Extended, Placed, Rejected) in Admin → Field Mappings before loading. Rows targeting an undefined status are silently rejected 62.

Common source → Bullhorn JobSubmission mapping

Source Destination
  • application_id
    JobSubmission.externalID

    Upsert key

  • candidate_id
    candidate.externalID (cross-ref)

    Candidate row must exist first or the JobSubmission row fails

  • job_id
    jobOrder.externalID (cross-ref)

    JobOrder row must exist first

  • applied_at
    dateWebResponse

    Required; Bullhorn stores in ET — convert source UTC offsets explicitly 71

  • current_stage
    status

    Map to defined Field Mappings value; undefined values fail silently 62

  • stage_history
    Sequence of update operations per status transition

    JobSubmission status history is built by repeated updates, not by importing a history table

  • source / where_from
    source field + categoryID

    Drives Source Effectiveness Report 157

JobSubmission status transition history is one of the most-asked-about pieces of an ATS migration and one of the least-supported on import. Bullhorn does not expose a status-history table — the audit trail is built by repeated updates that each generate a history row 134. To preserve the timeline, capture each transition as (submission_id, status, timestamp, user) and replay updates in date order, accepting that the *modifying user* will be the migration user, not the original recruiter.

Alternatively, store the legacy stage history verbatim in a Custom Text Block field on JobSubmission so it stays searchable, and let live history accrue forward from cutover.

Candidate sources, EEOC and Categories

Candidate source tracking lives on the source field plus a categoryID link to a Source category list. EEOC data (ethnicity, gender, veteran, disability) lives on dedicated Candidate fields and is reportable through Bullhorn's Standard Source Effectiveness and Diversity reports 157. Treat EEOC fields as sensitive: confirm the source export is permitted under your retention policy and that the destination's privacy posture matches 130.

Categories in Bullhorn are the tagging system — industry, skills, certifications, geographic specialties. They attach to Candidate, ClientContact, JobOrder and Placement. Pre-create the parent Category lists in Admin → Categories, then resolve source tags to Bullhorn Category IDs via a lookup table during transform. Mismatched Category names silently drop on import.

Resumes and file attachments

Resume and attachment migration is its own pipeline that runs *after* the Candidate rows land. The Custom Import wizard does not handle files; the supported paths are Data Loader's convertAttachments and loadAttachments commands, which read a CSV of {candidate externalID, file path, file type} 74, and the Bullhorn Automation Resume Upload survey for candidate self-service backfill 106.

File-size cap for record attachments is 20 MB per file; supported types include doc, docx, odt, rtf, html, pdf, txt for resume parsing, plus PDF, MS Word, Excel and images for general attachments 7273. For large resume estates, most agencies keep originals in S3 or Azure Blob, load only the latest resume into Bullhorn, and store the archive deep link in a Custom URL field.

Notes, Tasks and Appointments — historical activity

Bullhorn supports importing Notes, Tasks and Appointments with their original dateAdded timestamps via Data Loader 52. Each Note carries an action (Call, Email, Meeting, General), commentingPerson (recruiter), commentsText (body), and one or more associations to Candidate, ClientContact, JobOrder or Placement parents.

Bullhorn Notes do not support rich-text formatting — pasting HTML, images or charts is rejected and only plain text survives 52. Convert source rich-text bodies to plain text or a Markdown-style structure during transform, otherwise the body arrives blank.

Custom-field mapping strategy

Resist the urge to map every source custom field one-to-one. Migrate only the custom fields used by an active recruiter workflow or report in the last 12 months. Excess fields weaken Field Mappings governance, slow Bullhorn Search indexing, and make Reporting harder to wire.

For picklist fields whose source values do not match the destination, either: (1) extend the destination drop-down in Admin → Field Mappings, (2) collapse adjacent values during transform, or (3) introduce a parallel Custom Text field holding the legacy value verbatim. Calculated / formula fields do not import — Bullhorn has no formula type, so source formulas must be replicated via Bullhorn Automation or pre-computed before export.

Audit trail, ownership and original timestamps

Bullhorn maintains an audit trail on the Activity tab of each record, with the modifying user recorded as the user that initiated the change 134. System-managed dateAdded and dateLastModified can be overwritten on creation via Data Loader for most entities — unusual for the category and a quiet win for historical timelines.

However, dateAdded is interpreted in Eastern Time on the Bullhorn server 2771. A source datetime of 2024-08-14 12:00 NZST round-trips as 2024-08-14 00:00 ET if you do not convert, shifting dates by up to 24 hours for non-US recruiters. Convert every datetime to ET explicitly during transform and document the convention in your migration spec.

Owner assignment during import works only if the username or CorporateUser ID exists at load time; rows whose owner cannot be resolved are imported with the load user as owner, silently breaking Pulse attribution and per-recruiter reporting 133.

Section 06

The pitfalls that derail Bullhorn migrations

Specific failure modes — ranked by impact, each tied to the exact Bullhorn mechanism that breaks.

High impact

Clicking *Next* twice on the Custom Import preview duplicates every row

The Custom Import wizard's preview screen has a documented warning: clicking *Next* more than once creates duplicate records 142. The screen appears unresponsive while the import queues server-side, so impatient operators click again, and every Candidate, Contact or Lead in the file is loaded twice. The mitigation is to click once, walk away, and verify in *Settings → Imports* before retrying. If you have already double-clicked, mass-delete the duplicates by the *Imported* status and re-run cleanly. 142

High impact

Picklist display labels rejected because Field Mappings uses internal values

Bullhorn drop-down fields — Candidate status, JobOrder status, JobSubmission status, Placement employmentType, every custom Drop-Down — match on the value defined in Admin → Field Mappings, not the display label users see in the UI. Rows that target an undefined or display-label-only value are silently dropped from the load with no field-level error 62. Pre-create every destination drop-down value, build a source-value → destination-value lookup, and validate the lookup against the live Field Mappings export before loading at scale. 62

High impact

Datetimes shift by up to 24 hours because Bullhorn stores in Eastern Time

Bullhorn stores every datetime in Eastern Time on the server, regardless of user timezone 2771. A source dateAvailable of 2024-08-14 12:00 NZST loaded with no offset is interpreted as ET midnight and stored as a date that appears one day earlier to NZ recruiters. The fix is to convert every datetime to ET during transform and send the time portion explicitly — Bullhorn ignores time on some Custom Date fields and reads it on Date-Time fields, so verify per-field type before loading 71. 71

High impact

JobSubmission and Placement loads fail because parent Candidates / JobOrders are not yet present

JobSubmission requires both a Candidate and a JobOrder to exist before the row can be created; Placement requires a JobSubmission. Loads that interleave parent and child entities, or that run them in source-export order rather than dependency order, return cross-reference errors and partial successes that are painful to recover. The fix is to load strictly in order: ClientCorporation, ClientContact, Candidate, JobOrder, JobSubmission, Sendout, Placement, Note, Appointment, files. Data Loader does not enforce ordering — your runbook has to.

Medium impact

Custom Objects must be provisioned by Bullhorn Support before any row can be loaded

Unlike custom fields, Custom Objects in Bullhorn cannot be created through the UI by a customer admin — they require a ticket to Bullhorn Support, who provisions the schema and configures department-level visibility 16160. Teams who discover this in week three of a migration lose a week to the support queue. Open the Custom Object ticket on day one of the project, well before the load is scheduled, and confirm the entity name, parent association and 55-field budget per object 16. 16

Medium impact

Resume Parser overwrites curated candidate fields on import

If Resume Parser is enabled on the destination account and you load resumes alongside Candidate rows, the parser will re-populate firstName, lastName, email, phone and skill Categories from the resume content — overwriting values you carefully migrated from the source 106. The fix is to either disable Resume Parser overwrite behaviour during the migration window via a Bullhorn Support ticket, or to load Candidates first, verify the data, then upload resumes as attachments rather than through the parser path. 106

Medium impact

Notes lose rich-text formatting and embedded media on import

Bullhorn Notes do not support HTML, embedded images, charts or rich-text formatting 52. Source systems that store recruiter notes as HTML blobs (Outlook-pasted emails, Word-formatted call summaries) lose all formatting when loaded — only plain text survives. The fix is to flatten HTML to plain text or to a Markdown-style structured format during transform, and to keep the original rich-text body archived in a Custom Text Block field on the parent record if downstream search depends on it. 52

Low impact

EEOC field migration without consent or retention audit

Candidate ethnicity, gender, veteran and disability fields are sensitive personal data under GDPR, US EEOC reporting requirements, and the Bullhorn privacy policy 130. Loading them into a new account without checking the destination's data-retention configuration and the original collection consent is the kind of mistake that surfaces in a compliance audit, not in QA. Confirm EEOC fields are in scope with legal before exporting, and document the lawful basis for each field's migration in the project record. 130

Section 07

Validation and cutover

What to verify after the import job, in what order — and how to fail safely when something is wrong.

1 Read-only Source goes write-frozen 2 Final delta Export incremental changes 3 Import Load into Bullhorn 4 Validate Reconcile + spot-check 5 Cut over Users on new system
Cutover sequencing — five gated phases between source read-only and full user access.

Validation is the bridge between Data Loader finishing and recruiters being allowed in. A three-stage validation works best: a 10 percent pilot load with recruiter spot-checks, the full load with real-time row-count monitoring, and a 30-day audit driven by Bullhorn Reporting. The most reliable signal is recruiters verifying their own desks — they know what right looks like better than any reconciliation script.

Build a reconciliation queries spreadsheet that compares source and destination on each of these counts. Anything outside a 0.5 percent variance gets investigated before users get login access.

  • Total Candidates loaded vs. source — minus deliberately excluded rows (stale, opt-out, duplicate emails).
  • Total ClientCorporations and ClientContacts loaded vs. source — checking that name-based collapse did not over-merge separate clients.
  • Total JobOrders per status vs. source, plus sum of salary per JobOrder.status — a non-trivial dollar variance signals a status-mapping error or currency-precision drop.
  • Total JobSubmissions per JobOrder vs. source, and JobSubmission counts per status — confirms the candidate pipeline shape per requisition matches.
  • Total Placements per employmentType (Contract, Contract-to-Hire, Perm) and sum of payRate per period — drives billing-side reporting from day one.
  • Notes, Tasks and Appointments per entity with dateAdded round-tripped — date-bucket the comparison to confirm timestamps were not silently shifted by the ET conversion 27.
  • Owner distribution per entity — group by owner.username and confirm no record landed unowned that should not have 133.
  • ExternalID integrity — count distinct externalIDs per entity; any nulls or duplicates indicate the upsert key was not consistently set 3.

On top of reconciliation, run a manual spot-check: pick 30 random Candidates across statuses and verify each field against the source UI. Pick five high-value JobOrders and trace the full association graph — ClientContact, Candidate, JobSubmission, Sendout, Placement, Notes. If discrepancies show up in three or more of the 30, halt the load, fix the root cause, and re-run by externalID upsert.

Bullhorn does not ship a native bulk-undo. The recovery path: every imported row carries a Custom Text field stamped *Import Batch ID*, and if catastrophe strikes, a mass-delete in Admin → Mass Update filtered by that batch ID rolls the import back 124127. Bullhorn Support can also perform a *Hard Delete of Existing Data*, but the operation is one-way and irreversible 127.

The real rollback strategy remains: export everything to S3 before the import starts, stamp every imported row with an Import Batch ID, and bulk-delete by that batch ID if recovery is needed.

Cutover sequencing: (1) source ATS goes read-only and the desk is notified; (2) a final delta export captures everything changed during the test window; (3) delta is loaded via Data Loader against externalID; (4) reconciliation runs; (5) recruiters get login access and a 48-hour hyper-care window; (6) source decommission is scheduled 30 to 90 days out, never same-day.

Section 08

Migration partners and tools

Bullhorn Marketplace partners, ETL vendors, specialist migration shops — what each is good for and how to choose.

The Bullhorn Marketplace lists 300+ pre-integrated technology partners and a smaller cohort of certified implementation consultancies that handle migration work 112. Bullhorn itself ships an in-house Implementation team that manages most small and mid-size agency migrations end-to-end, typically two to six weeks of elapsed work for mid-size firms 104.

For agencies that want external help, the Bullhorn Marketplace lists a tier of long-standing Bullhorn-focused consultancies 112. A separate band of specialist migration vendors focuses narrowly on Bullhorn import paths — published timelines run 1 to 3 days for a full migration on smaller datasets, and resume-extraction-as-a-service offerings handle parsing from email inboxes plus bulk loading.

On the ETL and iPaaS side, Fivetran, Airbyte, Workato and Integrate.io all support Bullhorn as source or destination. Their role is rarely the migration itself — it is the staging layer that lands source data into a warehouse, the transformation layer that converts picklist values and resolves owner usernames, and the ongoing-sync layer post-migration.

Workato is common where the migration is bundled with workflow automation rebuilds; Fivetran and Airbyte are common picks for warehouse-first teams that want to reverse-ETL into Bullhorn.

Managed-migration cost ranges vary widely. A clean homegrown-database-to-Bullhorn load of under 25,000 Candidates with no resume backfill often lands in the $2,000–$10,000 range on top of the Bullhorn license. A Crelate-to-Bullhorn or Greenhouse-to-Bullhorn project with JobSubmission history, Custom Objects, resume migration and Automation rebuilds typically runs $15,000–$60,000, driven by record count, custom-field complexity and resume-file volume.

For teams that want to outsource the migration end-to-end, FlitStack specialises in Bullhorn migrations and handles the field mapping, externalID upsert design, resume-file pipeline, JobSubmission history rebuild and validation work in Sections 5 and 7. Pricing is fixed-fee, based on record count and source platform, with separate line items for Custom Objects and resume-file depth so scope is transparent before signature.

This is one of several legitimate paths — the right choice depends on whether you want the in-house Bullhorn Implementation team, a Marketplace partner like Newbury, an iPaaS-first approach with Fivetran or Workato, or a specialist migration vendor. Explore FlitStack →

Section 09

Frequently asked questions

The questions every Bullhorn migration team works through before they sign the scope.

References

Sources

  1. 1 Bullhorn, Inc. — Wikipedia
  2. 3 Bullhorn Data Loader — GitHub
  3. 12 Bullhorn Open-Source Projects
  4. 14 Bullhorn — GitHub organisation
  5. 16 Understanding Custom Objects — Bullhorn ATS
  6. 17 Understanding Bullhorn Field Edit Types
  7. 27 Date/timestamp convert issue — Bullhorn Support Forums
  8. 29 A guide to data hygiene for staffing firms — Bullhorn
  9. 32 How to Import Data into Bullhorn — Bullhorn ATS
  10. 52 Managing Notes in Bullhorn ATS
  11. 62 Custom Import Troubleshooting — Bullhorn ATS
  12. 71 How to get the correct default value of date time fields — Bullhorn Support Forums
  13. 72 Adding Images, PDFs, or Documents with the File Manager — Bullhorn
  14. 73 File size limit for email and record file attachments — Bullhorn
  15. 74 Bullhorn Data Loader — Commands wiki
  16. 92 How to Refresh a Sandbox — Bullhorn
  17. 104 Switching to Bullhorn: What to expect — Bullhorn Blog
  18. 106 Bullhorn Resume Upload — Bullhorn Automation
  19. 112 Newbury Partners — Bullhorn Marketplace
  20. 124 Export and Mass Delete Contacts — Bullhorn
  21. 127 Hard Delete of Existing Data — Bullhorn
  22. 130 Privacy Policy — Bullhorn
  23. 133 Owner Assignment Rules — Bullhorn Automation
  24. 134 Bullhorn System Information — audit trail behaviour
  25. 142 How to Import Data into Bullhorn — Custom Import wizard
  26. 156 Partial Sandbox — Bullhorn
  27. 157 Understanding Bullhorn Standard Reports
  28. 160 Custom Objects — Bullhorn

Need help running this migration?

FlitStack AI runs Bullhorn ATS & CRM migrations end-to-end.

Fixed-fee pricing, a hands-on migration engineer, full field mapping and validation. The work described in this guide — done for you.