When a person rewords a job title on their profile, the old wording doesn't always go away. Some data sources keep the earlier version as history and add the new wording as a separate entry. A record built from those sources can then list one job twice, under two titles, with the same start date.
Title edits are one of several ways a work history picks up duplicate roles. The others come from combining records: two contacts for the same person combined in a CRM, a second enrichment vendor added beside the first, or an old stored profile combined with a fresh one. This guide explains where the extra roles come from, how to find them in your own data, and how to deduplicate them without deleting jobs a person really held twice.
Why one job shows up twice
A work history is a list of roles. Each role has a company, a title, a start date and usually an end date. When two copies of a person's history are combined, software has to decide which roles in one list match roles in the other. If it compares company name and title, two copies that describe the same job in slightly different words won't match, and the job is kept twice.
These differences break the match:
- The company was renamed. A role stored under the company's old name in an older copy and under its new name in a newer copy looks like two employers.
- The title was reworded. "Growth Lead" and "Head of Growth" at the same company, starting the same month, are usually one job described two ways.
- The company is linked in one copy and typed in the other. One entry points to the company's profile page and the other only has the name as text, so a match on the company link fails.
- The company appears under two identifiers. The same company page can be referenced by a numeric ID or by its named URL.
- One copy is open-ended. An older copy recorded the job while the person still held it, so it has no end date. Next to the dated copy, it makes a past job look current.
- The dates are a month apart, or one copy has only the year. A start of "2024" and a start of "September 2024" describe the same job, but an exact date match treats them as different.
Sometimes the person entered the same job twice on their own profile, for example once with the company typed in and once linked to the company page. That duplicate is in the profile itself, so a system that copies the profile accurately returns both entries.
How to spot duplicate roles in your own data
Run these checks over the work histories you store:
- Same company and same title, with overlapping dates. This is the clearest sign of a duplicate.
- Two current roles at the same company. Check whether one of them is an open-ended copy of a role that also appears with an end date.
- Same company and same start month, different titles. This is often a reworded title. It can also be two titles a person really held at once, such as founder and CEO.
- One copy with a company link and one without. When the titles and dates also line up, the unlinked copy is usually the extra one.
- A current role at a company the person left. A false current role shows up as a missed job change, or as a contact still assigned to the old account.
Duplicate roles can also create duplicate contacts. If your CRM sync creates a contact for each current employer, an open-ended copy of an old job can put a second contact for the same person under the company they left. To find those, group contacts by the person's profile URL instead of by name and company. The profile URL usually stays the same when the person changes jobs, so two contacts with the same URL are almost always the same person. The guide to CRM data enrichment best practices covers how to check identity before enrichment writes to a contact.
Rules for deduplicating roles, with a worked example
Compare every pair of roles in one person's history. Treat two roles as the same job only when all three of these hold:
- Same company. If both roles have a company link, compare the links. If one or both lack a link, compare names after you lowercase them, remove punctuation, and remove legal suffixes such as Inc., LLC, and Ltd. If one copy uses a numeric company ID and the other a named URL, convert both to the same form first. The Company ID Lookup endpoint returns the named form for a numeric ID. The Person Profile endpoint already matches both forms in its own response, so you need this step when you combine its data with records from another source. Don't count one name containing the other as a match on its own, or a role at a parent company and a role at one of its divisions will look like one employer.
- Same title. Lowercase it and collapse extra spaces. You can expand common abbreviations ("Sr." to "Senior"). Don't match titles more loosely than that, because different titles at one company with different start dates are usually a promotion.
- Overlapping dates. Compare by month. Treat a missing end date as "present." If a role has no start date, send the pair to review instead of deciding automatically.
When two roles are the same job, keep one row. Prefer the copy with a real end date over an open-ended one when they start in the same month, and prefer the copy with a company link. Then fill any empty fields in the kept row from the other copy.
Here is a work history for a fictional engineer, combined from two sources:
| Row | Title | Company | Company link | Start | End |
|---|---|---|---|---|---|
| 1 | Software Engineer | Fabrikam, Inc. | Yes | 2016-06 | 2019-08 |
| 2 | Software Engineer | Fabrikam | No | 2016-06 | Present |
| 3 | Senior Software Engineer | Fabrikam, Inc. | Yes | 2019-09 | 2021-04 |
| 4 | Staff Engineer | Contoso Ltd | Yes | 2021-05 | 2023-10 |
| 5 | Principal Engineer | Contoso Ltd | Yes | 2021-05 | 2023-10 |
| 6 | Senior Software Engineer | Fabrikam, Inc. | Yes | 2023-11 | Present |
Applying the rules:
- Rows 1 and 2 are one job. "Fabrikam, Inc." and "Fabrikam" match once the suffix is removed. The title is the same, and row 2's open end overlaps row 1. Keep row 1, which has the end date and the company link. Without this step, the engineer looks like a current Fabrikam employee in two roles.
- Rows 3 and 6 are two separate periods. The company and title match, but row 3 ends in April 2021 and row 6 starts in November 2023. The engineer left Fabrikam and came back in the same role. Keep both.
- Rows 1 and 3 are a promotion. The company is the same and the titles differ, so they are different roles. Keep both.
- Rows 4 and 5 go to review. They have the same company and the same dates but different titles. One may be an older wording of the other, or the engineer may have held both titles. If one row came from an older copy of the profile, keep the newer wording. Otherwise keep both.
The cleaned history has five rows, or four if review shows rows 4 and 5 are one job.
The same rules in Python, written for the experiences array that the Person Profile endpoint returns:
import re
LEGAL_SUFFIXES = {"inc", "llc", "ltd", "corp", "co", "gmbh", "plc"}
def company_name(role):
words = re.sub(r"[^\w\s]", " ", (role.get("company") or "").lower()).split()
return " ".join(w for w in words if w not in LEGAL_SUFFIXES)
def same_company(a, b):
link_a = a.get("company_linkedin_profile_url")
link_b = b.get("company_linkedin_profile_url")
if link_a and link_b:
return link_a.rstrip("/").lower() == link_b.rstrip("/").lower()
return company_name(a) == company_name(b)
def title_key(role):
return " ".join((role.get("title") or "").lower().split())
def month(date, default_month):
if not date or not date.get("year"):
return None
return date["year"] * 12 + (date.get("month") or default_month)
def overlaps(a, b):
start_a, start_b = month(a.get("starts_at"), 1), month(b.get("starts_at"), 1)
if start_a is None or start_b is None:
return None # no start date: send to review
end_a = month(a.get("ends_at"), 12) or float("inf") # no end date means present
end_b = month(b.get("ends_at"), 12) or float("inf")
return start_a <= end_b and start_b <= end_a
def compare(a, b):
if not same_company(a, b):
return "different roles"
if title_key(a) == title_key(b):
overlap = overlaps(a, b)
if overlap is None:
return "review"
return "same job, keep one" if overlap else "separate periods, keep both"
if a.get("starts_at") == b.get("starts_at") and a.get("ends_at") == b.get("ends_at"):
return "review"
return "different roles"
When a repeated role is real history
A repeated company and title is not always a duplicate. These patterns are real history, and a rule that matches only on company and title deletes them:
- Boomerang hires. People leave a company and return, sometimes in the same role. The two periods don't overlap.
- Repeated seasonal roles. A ski instructor who works the winter season at the same resort for three years has three separate roles with the same title.
- Promotions. Consecutive roles at one company with different titles are a career path. Keep each step.
- Two titles at once. A founder who is also CEO, or an engineer who is also a team lead, may list both with the same dates.
- A parent company and its units. A role at a company and a later role at one of its divisions are two employers on the profile.
For two roles with the same title, overlapping dates separate a duplicate from a repeat. Two periods of the same job can't overlap in time, and a duplicate almost always does. Roles with different titles and the same dates, like the founder who is also CEO, go to the review step from the worked example.
What Enrich Layer does about duplicate roles today
Enrich Layer builds each Person Profile from more than one source, so it has to solve the same matching problem before it returns work history. Since October 29, 2026, the Person Profile endpoint follows the rules below when it combines work history from different sources.
Duplicate roles are now rare, and most of the ones that remain come from a source's own data or are on the profile itself. The endpoint works like this:
- One row per entry across sources. When two sources describe an entry at the same company with the same start month, the endpoint returns one row. The newer title and dates are used, and the older copy only fills empty fields such as the company link. This also removes open-ended second copies of past jobs that made them look current.
- One company, two page URLs. The endpoint matches the named and the numeric company page URLs to one company.
- Entries the person removed stay removed. An older stored copy doesn't add back roles, schools or volunteer entries that are gone from the current profile, in sections the current source shows. Older history that the current source can't see is kept.
- Repeat periods are kept. Separate periods at the same company with the same title come back as separate rows, in date order.
- Different roles from one source stay apart. When the current source lists two titles at one company with the same dates, such as founder and CEO, both come back. A parent company and its business units also stay separate.
Some duplicates can still appear:
- The profile lists the role twice. When a person has listed the same role twice, the endpoint returns both entries as they appear on the profile.
- A source's own data has two copies. Some duplicates come from a single source's record of the profile. Enrich Layer reports them to that source, and they clear when the source refreshes its record. This applies to work history, education and volunteering alike.
- Company names written in different forms. Abbreviations and different legal suffixes, such as "Inc." in one copy and "Ltd" in the other, may not be matched.
- Back-to-back periods. When one period ends in the same month the next one starts, the two come back as one row.
These changes apply to every request, including requests with default settings. You don't need special settings or a forced refresh to get the corrected work history for a profile you fetched before. Copies you already saved in your own CRM or database don't change until you request the profile again, so run the checks above on them.
Check your own records
The checks and rules above work on any work history, whatever its source. To check a sample of your contacts, fetch their profiles from the Person Profile endpoint and run the comparison over each experiences array.
If you’re comparing providers, add a duplicate-role count to your test set, as described in how to test and evaluate a data enrichment dataset. For the wider picture of keeping records consistent, see what data integrity actually means.
