How to Deduplicate and Visualize Talent Pools Across ATS Platforms Using Unified's ATS API
February 25, 2026
Recruiting teams rarely operate in a single system.
They may use:
- Different ATS platforms across regions
- Multiple ATS instances for separate business units
- An ATS plus an internal talent CRM
- A sourcing database layered on top
From the recruiter's perspective, this creates blind spots:
- Are we sourcing candidates who already exist in another system?
- How many unique candidates do we actually have?
- How many are active vs stale?
- Where are candidates getting stuck in the funnel?
If you're building a recruiting product, this is a customer-facing feature:
A unified talent pool view across ATS platforms.
This guide walks through how to implement talent pool deduplication and visibility using Unified's ATS API.
This is a read + analytics workflow. No write-back required.
The Mental Model
Your product becomes a candidate identity layer.
It:
- Pulls candidates from one or more ATS connections
- Normalizes the candidate object
- Deduplicates across systems
- Aggregates application stages
- Computes recency and stale metrics
- Visualizes funnel distribution
Unified provides normalized candidate and application models across supported ATS integrations.
You provide:
- Identity resolution logic
- Cross-system merging
- Analytics and visualization
Step 1: Pull Candidates from Each ATS Connection
Start by listing candidates for each connected ATS.
Endpoint
GET /ats/{connection_id}/candidate
Node SDK Example
const candidates = await sdk.ats.listAtsCandidates({
connectionId,
limit: 100,
offset: 0,
sort: 'updated_at',
order: 'asc',
});
Important fields from AtsCandidate:
idemails[]telephones[]link_urls[]first_name,last_namecompany_nametitletags[]metadata[]updated_atcreated_at
Store candidates in your own database. Do not run analytics directly off live API calls.
Step 2: Normalize Candidate Identity
You need a canonical identity key across systems.
Use the following priority order:
1️⃣ Email (primary key)
emails[] contains structured email objects:
{
"name": "Jane Doe",
"email": "jane@example.com",
"type": "WORK"
}
Deduplicate primarily on normalized email address:
- Lowercase
- Trim whitespace
- Remove aliasing patterns if appropriate (e.g., Gmail dots)
Email is the most reliable cross-system identifier.
2️⃣ LinkedIn URL
link_urls[] includes:
URLs for web pages containing additional material about the candidate (LinkedIn, other social media, articles, etc.)
Extract LinkedIn URLs and normalize:
- Remove tracking parameters
- Standardize domain variants
This becomes your secondary dedupe key when email is missing.
3️⃣ Phone Number (fallback)
From telephones[], normalize:
- Strip formatting
- Standardize country codes
Phone numbers are useful but less consistent.
Step 3: Build a Canonical Candidate Record
After dedupe, merge candidate records into a single canonical entity:
CanonicalCandidate
- primary_email
- linkedin_url
- phones[]
- first_seen_at
- last_updated_at
- ats_sources[]
- candidate_ids_by_connection
Store:
- All ATS candidate IDs
- Which connection they belong to
- The most recent
updated_atacross systems
This lets you:
- Avoid re-sourcing known candidates
- Show a unified profile
- Link back to each ATS record
Step 4: Merge Applications for Funnel Visibility
Now pull applications.
Endpoint
GET /ats/{connection_id}/application
Example
const applications = await sdk.ats.listAtsApplications({
connectionId,
limit: 100,
offset: 0,
});
From AtsApplication, use:
candidate_idstatusapplied_atrejected_athired_at
Map each application to your canonical candidate record.
Step 5: Visualize Stage Distribution
Use status for stage aggregation.
To retrieve available stage vocabulary:
GET /ats/{connection_id}/applicationstatus
From AtsStatus:
statusoriginal_statusdescription
Now compute:
- Count of candidates per
status - Count per ATS connection
- Cross-ATS aggregated funnel
Example output:
| Stage | Count |
|---|---|
| NEW | 125 |
| REVIEWING | 82 |
| FIRST_INTERVIEW | 34 |
| OFFERED | 9 |
| HIRED | 6 |
This becomes a funnel visualization in your product.
Step 6: Highlight Stale Candidates
Use updated_at on the candidate object.
Define stale logic, for example:
- No updates in 90 days
- No active applications
- No interview scheduled
Example:
const isStale = daysSince(candidate.updated_at) > 90;
You can also compute:
- Days since last application
- Days since last interview
- Days since stage change (if you track it yourself)
Unified does not expose historical stage transitions, so you must track changes over time if you need them.
Step 7: Merge Internal + ATS Data
If your product also has:
- Internal CRM candidates
- Sourced prospects
- Event leads
You can unify them using the same identity logic:
- Phone
Then show:
- 'Already in ATS (Greenhouse)'
- 'Previously applied in EMEA instance'
- 'Rejected 6 months ago'
This prevents duplicate outreach and improves recruiter efficiency.
Architecture Recommendation
- Initial full sync of candidates and applications
- Store in internal database
- Deduplicate into canonical identities
- Compute analytics from your own DB
- Refresh via:
- Webhooks (if enabled)
- Or incremental polling using
updated_gte
Do not recompute dedupe in real time on every page load.
Why This Matters to Your Customers
Talent pool visibility helps customers:
- Avoid sourcing duplicates
- Reduce recruiter fatigue
- Understand true funnel size
- Identify pipeline bottlenecks
- Re-engage dormant candidates
By using Unified's normalized ATS API as a read layer, you can provide cross-platform visibility without building custom integrations for every ATS.
Closing Thoughts
Deduplicating and visualizing talent pools across ATS platforms is not an integration problem.
It's an identity and analytics problem.
Unified provides:
- Structured candidate models
- Standardized application objects
- Stage vocabulary
- Consistent pagination and incremental updates
Your product layers on:
- Identity resolution
- Canonical merging
- Funnel analytics
- Staleness detection
- Recruiter-facing insights
That combination turns fragmented ATS data into actionable talent intelligence.