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'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.
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.
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
- 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
- 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
- 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.
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
salaryper 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
payRateper period — drives billing-side reporting from day one. - Notes, Tasks and Appointments per entity with
dateAddedround-tripped — date-bucket the comparison to confirm timestamps were not silently shifted by the ET conversion 27. - Owner distribution per entity — group by
owner.usernameand 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 Bullhorn, Inc. — Wikipedia
- 3 Bullhorn Data Loader — GitHub
- 12 Bullhorn Open-Source Projects
- 14 Bullhorn — GitHub organisation
- 16 Understanding Custom Objects — Bullhorn ATS
- 17 Understanding Bullhorn Field Edit Types
- 27 Date/timestamp convert issue — Bullhorn Support Forums
- 29 A guide to data hygiene for staffing firms — Bullhorn
- 32 How to Import Data into Bullhorn — Bullhorn ATS
- 52 Managing Notes in Bullhorn ATS
- 62 Custom Import Troubleshooting — Bullhorn ATS
- 71 How to get the correct default value of date time fields — Bullhorn Support Forums
- 72 Adding Images, PDFs, or Documents with the File Manager — Bullhorn
- 73 File size limit for email and record file attachments — Bullhorn
- 74 Bullhorn Data Loader — Commands wiki
- 92 How to Refresh a Sandbox — Bullhorn
- 104 Switching to Bullhorn: What to expect — Bullhorn Blog
- 106 Bullhorn Resume Upload — Bullhorn Automation
- 112 Newbury Partners — Bullhorn Marketplace
- 124 Export and Mass Delete Contacts — Bullhorn
- 127 Hard Delete of Existing Data — Bullhorn
- 130 Privacy Policy — Bullhorn
- 133 Owner Assignment Rules — Bullhorn Automation
- 134 Bullhorn System Information — audit trail behaviour
- 142 How to Import Data into Bullhorn — Custom Import wizard
- 156 Partial Sandbox — Bullhorn
- 157 Understanding Bullhorn Standard Reports
- 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.
