Guide · Python · SDK · REST API

ORCID Validation in Python — Researcher Identifier Lookup

Every researcher can have an ORCID — a persistent digital identifier that disambiguates authors across journals, institutions, and funding bodies. Here's how ORCIDs are structured, why validation matters, and how to validate and look up any ORCID in your Python application.

1. What is an ORCID?

ORCID stands for Open Researcher and Contributor ID. It is a persistent digital identifier that uniquely distinguishes individual researchers — solving the name ambiguity problem that plagues academic publishing. Two researchers named "J. Smith" at the same university are clearly distinguished by their ORCIDs.

ORCIDs are managed by the ORCID organisation, a non-profit launched in 2012. The registry now holds over 18 million identifiers and is integrated into the submission workflows of most major publishers, funders, and institutional systems worldwide.

ORCIDs are used by publishers (Elsevier, Springer Nature, Wiley), funding agencies (NIH, ERC, UKRI), institutional repositories, and preprint servers. Many journal submission systems now require an ORCID at manuscript submission time.


2. ORCID structure

An ORCID iD is a 16-digit number displayed in four groups of four, separated by hyphens. The final character is a check digit calculated using the ISO 7064 MOD 11-2 algorithm, which can be a digit (0-9) or the letter X (representing the value 10).

ORCID anatomy

0000-0002-1825-0097
0000Group 1
0002Group 2
1825Group 3
0097Group 4 (incl. check digit)

Check digit calculation

The last character is computed using ISO 7064 MOD 11-2 over the first 15 digits. If the remainder maps to 10, the check digit is represented as X. This detects single-digit errors and all transposition errors.

FormatExampleNotes
XXXX-XXXX-XXXX-XXXX0000-0002-1825-0097Standard display format
XXXX-XXXX-XXXX-XXXX0000-0001-5109-3700Check digit is 0
XXXX-XXXX-XXXX-XXXX0000-0002-1694-233XCheck digit is X (value 10)
ℹ️ORCIDs are ISNI-compatible — they occupy a reserved block within the International Standard Name Identifier (ISNI) space. The 16-digit format is shared, but ORCID iDs always begin with 0000 in practice.

3. Why ORCID validation matters

ORCIDs are critical infrastructure for research identity management. Invalid or malformed ORCIDs cause real problems in production systems:

Grant applications

Funding agencies (NIH, NSF, ERC, UKRI) increasingly require valid ORCIDs on grant applications. A malformed ORCID can delay or invalidate a submission. Automated systems that pre-populate applicant profiles depend on correct identifiers to link prior publications and funding history.

Journal submissions

Most major publishers require ORCIDs during manuscript submission. Editorial systems use the ORCID to auto-fill author details, link to reviewer profiles, and track publication records. An invalid ORCID breaks this chain and can cause authorship attribution errors that persist in the published record.

Institutional repositories

Universities use ORCIDs to link researchers to their institutional profiles, publications, and datasets. CRIS systems (Pure, Symplectic, VIVO) rely on ORCIDs as the primary key for researcher disambiguation. Importing records with invalid ORCIDs creates ghost profiles and breaks reporting.

Data integrity

Research information systems that aggregate data from multiple sources use ORCIDs for deduplication. A single transposed digit can split one researcher into two records or merge two researchers into one — both scenarios are costly to fix.


4. Basic validation vs profile lookup

The IsValid ORCID API offers two modes of validation. Basic validation checks the ORCID format, verifies the ISO 7064 check digit, and returns the formatted identifier with its URI. Profile lookup goes further and retrieves the researcher's public profile from the ORCID registry.

Basic (default)

  • Format validation
  • ISO 7064 MOD 11-2 check digit verification
  • Formatted ORCID (XXXX-XXXX-XXXX-XXXX)
  • ORCID URI

With lookup=True

  • Everything from basic, plus:
  • Whether the profile was found
  • Given names and family name
  • Current organisation
Use basic validation for fast input checks at form submission time. Use profile lookup when you need to verify the researcher's identity, pre-fill author details, or confirm institutional affiliation.

5. The right solution

The IsValid ORCID API handles format validation, check digit verification, formatting, and optional profile retrieval in a single call.

ISO 7064
Check digit
MOD 11-2 verification
auto-formatted
Formatting
XXXX-XXXX-XXXX-XXXX
optional lookup
Profile
name, organisation

Full parameter reference and response schema: ORCID Validation API docs →


6. Python code example

from isvalid import create_client

iv = create_client(api_key=os.environ["ISVALID_API_KEY"])

# ── Basic validation ────────────────────────────────────────────────────────

result = iv.orcid("0000-0002-1825-0097")

print(result["valid"])     # True
print(result["formatted"]) # '0000-0002-1825-0097'
print(result["uri"])       # 'https://orcid.org/0000-0002-1825-0097'

# ── With profile lookup ─────────────────────────────────────────────────────

lookup = iv.orcid("0000-0002-1825-0097", lookup=True)

print(lookup["profile"]["givenNames"])   # 'Josiah'
print(lookup["profile"]["familyName"])   # 'Carberry'
print(lookup["profile"]["organization"]) # 'Brown University'

In a researcher onboarding pipeline:

# Validate ORCIDs before importing researcher records
def import_researchers(rows: list[dict]) -> list[dict]:
    results = []

    for row in rows:
        if not row.get("orcid"):
            results.append({**row, "orcid_status": "missing"})
            continue

        check = iv.orcid(row["orcid"], lookup=True)

        if not check["valid"]:
            results.append({**row, "orcid_status": "invalid"})
            continue

        results.append({
            **row,
            "orcid": check["formatted"],
            "given_names": check.get("profile", {}).get("givenNames"),
            "family_name": check.get("profile", {}).get("familyName"),
            "organization": check.get("profile", {}).get("organization"),
            "orcid_status": "valid",
        })

    return results
Always store the formatted ORCID (e.g. 0000-0002-1825-0097) rather than a bare digit string. You can construct the URI at display time by prepending https://orcid.org/.

7. cURL example

Basic ORCID validation:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/orcid?value=0000-0002-1825-0097"

With profile lookup:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/orcid?value=0000-0002-1825-0097&lookup=true"

ORCID without hyphens:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/orcid?value=0000000218250097"

Invalid ORCID:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/orcid?value=0000-0002-1825-0000"

8. Understanding the response

Basic validation (valid ORCID):

{
  "valid": true,
  "formatted": "0000-0002-1825-0097",
  "uri": "https://orcid.org/0000-0002-1825-0097"
}

With profile lookup:

{
  "valid": true,
  "formatted": "0000-0002-1825-0097",
  "uri": "https://orcid.org/0000-0002-1825-0097",
  "profile": {
    "found": true,
    "givenNames": "Josiah",
    "familyName": "Carberry",
    "organization": "Brown University"
  }
}

Invalid ORCID:

{
  "valid": false
}
FieldTypeDescription
validbooleanWhether the ORCID has a valid format and check digit
formattedstringThe ORCID in display format (XXXX-XXXX-XXXX-XXXX)
uristringFull ORCID URI (https://orcid.org/...)
profileobjectOnly present when lookup=True
profile.foundbooleanWhether a public profile exists for this ORCID
profile.givenNamesstringThe researcher's given (first) names
profile.familyNamestringThe researcher's family (last) name
profile.organizationstringThe researcher's current organisation affiliation

9. Edge cases

(a) Check digit X

When the ISO 7064 MOD 11-2 algorithm produces a remainder of 10, the check digit is represented as the uppercase letter X. This is valid and must be accepted by any ORCID validation logic. The API handles this correctly and will normalise lowercase x to uppercase in the formatted output.

# ORCID with check digit X
result = iv.orcid("0000-0002-1694-233X")
print(result["valid"])     # True
print(result["formatted"]) # '0000-0002-1694-233X'

(b) URL vs bare ID

Researchers often share their ORCID as a full URL (https://orcid.org/0000-0002-1825-0097) rather than the bare identifier. If your system collects ORCIDs from user input, strip the URL prefix before passing it to the API.

import re

# Strip the ORCID URL prefix before validation
def extract_orcid(raw: str) -> str:
    return re.sub(r"^https?://orcid\.org/", "", raw, flags=re.IGNORECASE).strip()

orcid = extract_orcid("https://orcid.org/0000-0002-1825-0097")
result = iv.orcid(orcid)
print(result["valid"])  # True

(c) Inactive profiles

An ORCID can be structurally valid (correct format and check digit) but belong to an inactive or deactivated profile. When using lookup=True, check the profile.found field to determine whether the profile is publicly accessible. A valid ORCID with found: false may indicate a deactivated account or a profile set to private.

(d) Hyphens and whitespace

The canonical display format uses hyphens between groups, but users may enter ORCIDs without hyphens or with spaces. The API accepts the bare 16-digit string and will return the properly formatted version. Always store the hyphenated format returned in the formatted field.


10. Summary

Validate ORCID format and check digit before storing — prevents broken researcher links
Store the hyphenated format (XXXX-XXXX-XXXX-XXXX) — construct URIs at display time
Use profile lookup to verify researcher identity and pre-fill author details
Strip URL prefixes and normalise whitespace before validation
Do not reject check digit X — it is a valid value representing 10 in ISO 7064
Do not assume a valid ORCID means an active profile — always check profile.found when using lookup

See also

Validate ORCIDs instantly

Free tier includes 100 API calls per day. No credit card required. Supports basic validation and profile lookup.