OAI-PMH
The Open Archives Initiative Protocol for Metadata Harvesting (OAI-PMH) is a standard protocol used by libraries, discovery services, institutional repositories, and academic indexes to collect publication metadata.
Academic Stack provides an OAI-PMH 2.0 endpoint for published journal articles. The endpoint exposes Dublin Core metadata and supports both complete and incremental harvesting.
OAI-PMH distributes bibliographic metadata. It does not transfer article PDFs or replace DOI registration, Crossref deposits, sitemaps, or Google Scholar metadata.
This guide uses https://www.publisher.com as the example publisher domain and https://www.yourjournal.com as the example journal domain. Replace these domains—and placeholders such as {journal_slug}—with the actual domain and journal slug for your deployment.
Repository URLs
The available repository URL depends on the Academic Stack deployment type.
Enterprise / Single-Publisher Deployment
The publisher-level repository includes articles from all public journals:
https://www.publisher.com/oai
Each public journal also has an independent repository:
https://www.publisher.com/journal/{journal_slug}/oai
For example:
https://www.publisher.com/journal/nature-science/oai
The publisher-level repository uses OAI sets (setSpec) to separate journals. The journal-level repository contains only the selected journal.
PaaS / SaaS Deployment
Each journal domain represents an independent OAI-PMH repository:
https://www.yourjournal.com/oai
Only articles belonging to the journal resolved from the current domain are returned. A journal ID or slug cannot be supplied to access another journal.
There is no publisher-level or platform-wide repository in the PaaS/SaaS deployment.
Quick Verification
Open the following URLs in a browser or OAI-PMH validation tool:
https://www.yourjournal.com/oai?verb=Identify
https://www.yourjournal.com/oai?verb=ListMetadataFormats
https://www.yourjournal.com/oai?verb=ListRecords&metadataPrefix=oai_dc
A successful response is XML with an <OAI-PMH> root element and an HTTP content type of:
Content-Type: application/xml; charset=UTF-8
Supported OAI-PMH Requests
Academic Stack supports all six OAI-PMH 2.0 verbs.
| Verb | Purpose | Required / Allowed arguments |
|---|---|---|
Identify | Returns repository name, base URL, administrator email, datestamp policy, and protocol information. | None |
ListMetadataFormats | Lists metadata formats supported by the repository. | Optional identifier |
ListSets | Lists journal sets in a publisher-level repository. | None |
ListIdentifiers | Returns record headers without Dublin Core metadata. | metadataPrefix or resumptionToken |
ListRecords | Returns record headers and Dublin Core metadata. | metadataPrefix or resumptionToken |
GetRecord | Returns one record by its OAI identifier. | identifier, metadataPrefix |
Both HTTP GET and POST requests are supported.
Metadata Format
Academic Stack currently supports:
metadataPrefix=oai_dc
The oai_dc response uses the standard Dublin Core namespace:
http://www.openarchives.org/OAI/2.0/oai_dc/
Field Mapping Reference
Article metadata is mapped as follows:
| Academic Stack field | Dublin Core element |
|---|---|
| Article title | dc:title |
| Each author | dc:creator (one element per author) |
| Each keyword | dc:subject (one element per keyword) |
| Abstract | dc:description |
| Publisher name | dc:publisher |
| Publication or online date | dc:date |
| Article type | dc:type |
| DOI URL | dc:identifier |
| Article landing-page URL | dc:identifier |
| Journal, volume, issue, and pages | dc:source |
| Journal language | dc:language |
Note: Only public, non-deleted articles belonging to public journals are exposed.
OAI Identifiers
Each article receives a stable OAI identifier:
oai:{PROJECT_NAME}:article:{article_id}
For example:
oai:academic_stack:article:345
The identifier is independent of:
- Journal domains
- Journal slugs
- DOI changes
- Volume and issue assignments
- Article landing-page URLs
An OAI identifier identifies the metadata record. It is not the DOI or the article URL. DOI and landing-page URLs are provided separately as dc:identifier values.
Example request:
https://www.yourjournal.com/oai?verb=GetRecord&metadataPrefix=oai_dc&identifier=oai:academic_stack:article:345
Datestamps and Incremental Harvesting
The OAI header <datestamp> represents the article record's metadata update time, in UTC:
<datestamp>2026-07-30T08:30:00Z</datestamp>
The repository uses second-level granularity:
YYYY-MM-DDThh:mm:ssZ
Harvesters can request records updated within a date range:
https://www.yourjournal.com/oai?verb=ListRecords&metadataPrefix=oai_dc&from=2026-07-01T00:00:00Z
https://www.yourjournal.com/oai?verb=ListRecords&metadataPrefix=oai_dc&from=2026-07-01T00:00:00Z&until=2026-07-30T23:59:59Z
The from and until boundaries are inclusive. Both values must use the repository's full UTC timestamp format, and from cannot be later than until.
Metadata updates may cause an existing record to appear in a later incremental harvest. The OAI identifier remains unchanged.
Pagination and Resumption Tokens
ListRecords and ListIdentifiers return up to 100 records per response.
When more records are available, the response contains a resumptionToken:
<resumptionToken cursor="0">
eyJ2ZXJiIjoiTGlzdFJlY29yZHMi...
</resumptionToken>
Use the returned token in the next request:
https://www.yourjournal.com/oai?verb=ListRecords&resumptionToken=eyJ2ZXJiIjoiTGlzdFJlY29yZHMi...
When using a resumptionToken:
- Send only
verbandresumptionToken. - Do not repeat
metadataPrefix,from,until, orset. - Continue requesting pages until no
resumptionTokenis returned. - Use the token within one hour.
The token preserves the repository scope and all filters from the original request.
Sets
Sets are available only from the Enterprise / single-publisher publisher-level repository.
List all journal sets:
https://www.publisher.com/oai?verb=ListSets
Example set:
<set>
<setSpec>journal:nature-science</setSpec>
<setName>Nature Science</setName>
</set>
Harvest one journal from the publisher repository:
https://www.publisher.com/oai?verb=ListRecords&metadataPrefix=oai_dc&set=journal:nature-science
Records returned by the publisher-level repository include the journal set in their headers:
<header>
<identifier>oai:academic_stack:article:345</identifier>
<datestamp>2026-07-30T08:30:00Z</datestamp>
<setSpec>journal:nature-science</setSpec>
</header>
Journal-level repositories do not use sets. A ListSets request to a journal-level repository returns noSetHierarchy.
Deleted Records
The repository currently declares:
<deletedRecord>no</deletedRecord>
Deleted or unpublished articles are not included in OAI responses, and the repository does not expose persistent deleted-record headers.
Harvesters should treat this repository according to the OAI-PMH deletedRecord=no policy.
Protocol Errors
Invalid requests return an OAI-PMH XML error rather than JSON or an HTML application error.
Common error codes include:
| Error code | Meaning |
|---|---|
badVerb | The verb is missing or unsupported. |
badArgument | A required argument is missing, malformed, or not allowed. |
cannotDisseminateFormat | The requested metadata format is unsupported. |
idDoesNotExist | The identifier does not exist in the current repository. |
noRecordsMatch | No records match the date, set, or repository filters. |
noSetHierarchy | The journal-level repository does not support sets. |
badResumptionToken | The token is invalid, expired, or belongs to another request scope. |
Example:
<error code="cannotDisseminateFormat">
Only oai_dc is supported.
</error>
Recommended Harvesting Workflow
For an initial full harvest:
- Send
Identify. - Send
ListMetadataFormats. - If using a publisher-level repository, send
ListSets. - Start
ListRecordswithmetadataPrefix=oai_dc. - Follow every
resumptionTokenuntil the final page. - Store each record's OAI identifier and datestamp.
For later incremental harvests:
- Record the end time of the previous successful harvest.
- Send
ListRecordswith an overlappingfromtimestamp to avoid missing updates near the boundary. - Follow all
resumptionTokenpages. - Update existing records by OAI identifier.
- Save the new successful harvest time.
When scheduling incremental harvesting, use a small overlap between runs—for example, request from several minutes before the last successful harvest time. OAI identifiers are stable, so duplicate results can be safely updated by identifier.
Troubleshooting
| Symptom | Recommended check |
|---|---|
noRecordsMatch | Confirm that the journal and articles are public and that the date range uses UTC second-level timestamps. |
cannotDisseminateFormat | Use metadataPrefix=oai_dc. |
badResumptionToken | Restart the harvest from the original request; tokens expire after one hour. |
idDoesNotExist | Confirm that the identifier belongs to the current journal repository and that the article remains public. |
noSetHierarchy | Use sets only with the single-publisher publisher-level /oai endpoint. |
| Article URL returns 404 | Confirm that the article is public and its volume, issue, and article number are valid. |
| Metadata changes are not visible | Confirm the article was saved after editing, then repeat the incremental request using its updated datestamp. |
Before submitting an OAI-PMH endpoint to an indexing service, verify Identify, ListRecords, pagination, article URLs, authors, dates, DOI values, and journal metadata.