🐍 PythonAcademic / Publishing
Academic Publishing Validation
Validate DOI, ORCID, ISBN and ISSN identifiers in Python using asyncio.gather for concurrent API calls. Enrich submissions with CrossRef metadata and ORCID author profiles.
Also available in Node.js
Contents
1. Academic identifiers covered
| Identifier | Endpoint | Use case |
|---|---|---|
| DOI | /v0/doi | Digital Object Identifier — links to published works |
| ORCID | /v0/orcid | Open researcher & contributor ID |
| ISBN | /v0/isbn | International Standard Book Number |
| ISSN | /v0/issn | International Standard Serial Number for journals |
2. Validate DOI
Use await iv.doi(value) (SDK) or GET /v0/doi?value=….
{ "valid": true, "doi": "10.1000/xyz123", "prefix": "10.1000", "suffix": "xyz123", "registrantCode": "1000", "registrant": "International DOI Foundation", "url": "https://doi.org/10.1000/xyz123", "metadata": { "found": true, "title": "Sample Article Title", "authors": ["Smith, J.", "Doe, A."], "publisher": "Example Publisher", "type": "journal-article", "issued": "2023-04" } }
- Check
valid— structural DOI format check - Check
metadata.foundfor CrossRef registration - Use
metadata.titleandmetadata.authorsfor auto-fill - Verify
metadata.typematches expected content type
3. Validate ORCID
Use await iv.orcid(value) (SDK) or GET /v0/orcid?value=….
{ "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" } }
- Check
valid— ISO 27729 checksum - Check
profile.found— registered with ORCID registry - Use profile data for author name auto-fill if profile is public
ℹ️Many researchers set their ORCID profile to private.
profile.found = false does not mean the ORCID is invalid — only that public profile data is unavailable.4. Validate ISBN
Use await iv.isbn(value) (SDK) or GET /v0/isbn?value=….
{ "valid": true, "format": "ISBN-13", "isbn10": "0306406152", "isbn13": "9780306406157" }
- Check
validandformat - Store both
isbn10andisbn13for cross-referencing
5. Validate ISSN
Use await iv.issn(value) (SDK) or GET /v0/issn?value=….
{ "valid": true, "issn": "20493630", "normalized": "2049-3630" }
- Check
valid— mod-11 checksum including X digit - Store
normalized(XXXX-XXXX) as canonical form
6. Parallel validation with asyncio.gather
Dynamically build the task dict based on which identifiers the submission includes, then run all concurrently.
import asyncio from isvalid_sdk import IsValidConfig, create_client config = IsValidConfig(api_key="YOUR_API_KEY") iv = create_client(config) async def validate_academic_submission(submission: dict): tasks = {} if submission.get("doi"): tasks["doi"] = iv.doi(submission["doi"]) if submission.get("orcid"): tasks["orcid"] = iv.orcid(submission["orcid"]) if submission.get("isbn"): tasks["isbn"] = iv.isbn(submission["isbn"]) if submission.get("issn"): tasks["issn"] = iv.issn(submission["issn"]) results = dict(zip(tasks.keys(), await asyncio.gather(*tasks.values()))) doi_r = results.get("doi") if doi_r and doi_r.valid and doi_r.metadata: print(f"Title: {doi_r.metadata.title}") print(f"Publisher: {doi_r.metadata.publisher}") orcid_r = results.get("orcid") if orcid_r and orcid_r.valid and orcid_r.profile: print(f"Author: {orcid_r.profile.given_names} {orcid_r.profile.family_name}") return results async def main(): result = await validate_academic_submission({ "doi": "10.1000/xyz123", "orcid": "0000-0002-1825-0097", "issn": "2049-3630", }) for key, val in result.items(): print(f"{key}: valid={val.valid}") asyncio.run(main())
7. Edge cases
DOI valid but not registered in CrossRef
⚠️DOIs can be structurally valid before being registered. For manuscript submission systems, require
metadata.found = True only for references, not for the manuscript being submitted.doi = await iv.doi(value) if doi.valid and not doi.metadata.found: # DOI format is correct, but not yet indexed by CrossRef warn("DOI not yet resolvable — proceed with caution")
ORCID check digit — X character
💡The last digit of an ORCID can be the letter X (representing 10 in the ISO 7064 MOD 11-2 algorithm). Always accept both numeric and X as the final character.
# IsValid handles this automatically, but ensure your DB schema # stores ORCID as VARCHAR(19), not a numeric type orcid = await iv.orcid("0000-0001-5109-3700") # ends in 0 orcid_x = await iv.orcid("0000-0002-1694-233X") # ends in X — still valid
ISBN-10 vs ISBN-13 normalization
isbn = await iv.isbn(user_input) if isbn.valid: # Always store the ISBN-13 form as the canonical identifier canonical = isbn.isbn13
8. Summary checklist
✓Run all identifier calls with asyncio.gather
✓Require metadata.found for reference DOIs
✓Handle private ORCID profiles gracefully
✓Store ISBN-13 as canonical form
✓Normalize ISSN to XXXX-XXXX format
✓Accept ORCID check digit X
✓Use ThreadPoolExecutor for sync fallback
✓Return field-level errors on validation failure