---
name: datacite-rest-api-ingestion
description: "How to get DataCite REST API data into a pandas DataFrame and Parquet: access, the request, the format, limits, quirks and terms."
---

# Load DataCite dataset DOIs for a search into pandas and Parquet

Source entry: https://signal-lab-jet.vercel.app/datasets/datacite-rest-api
Provider: https://support.datacite.org/docs/rest-api
Checked against the provider's documentation: 2026-11-03

## Access
- No key for retrieval (the Public API). Add mailto=your address, or an email in the User-Agent, for the identified rate tier.
- Base URL https://api.datacite.org; /dois takes query, resource-type-id, page[size], page[cursor] and other filters.

## Request
```bash
curl 'https://api.datacite.org/dois?query=climate&resource-type-id=dataset&page%5Bsize%5D=100&disable-facets=true&mailto=you@example.org'
```

## Format
JSON:API: data is a list of DOI objects whose attributes carry doi, titles, creators, publisher, publicationYear, types (resourceTypeGeneral), rightsList, relatedIdentifiers and url; meta carries total and facets; links carries next.

## Load into pandas and Parquet
Needs Python 3 with requests, pandas and pyarrow.
```python
"""DataCite DOI metadata (datasets) -> pandas -> Parquet. No key; a mailto parameter puts you in the 'identified' rate tier."""
import requests
import pandas as pd

params = {"query": "climate", "resource-type-id": "dataset", "page[size]": 100,
          "disable-facets": "true", "mailto": "you@example.org"}  # replace with your own address
r = requests.get("https://api.datacite.org/dois", params=params, timeout=60)
r.raise_for_status()
js = r.json()
rows = []
for d in js["data"]:
    a = d["attributes"]
    rows.append({
        "doi": a.get("doi"),
        "title": (a.get("titles") or [{}])[0].get("title"),
        "publisher": a["publisher"] if isinstance(a.get("publisher"), str) else (a.get("publisher") or {}).get("name"),
        "year": a.get("publicationYear"),
        "resource_type": (a.get("types") or {}).get("resourceTypeGeneral"),
        "rights": "; ".join(x.get("rightsIdentifier") or x.get("rights", "") for x in a.get("rightsList", [])),
        "url": a.get("url"),
    })
df = pd.DataFrame(rows)
df.to_parquet("datacite_datasets.parquet", index=False)
print(len(df), "dataset DOIs on this page of", js["meta"]["total"], "matches")
```
Run by us on 2026-11-03 (python 3.13.5 | pandas 3.1.0 | pyarrow 26.0.0 | requests 2.34.2). Result: 100 dataset DOIs on this page of 222072 matches

## Limits
- 500 requests per 5 minutes per IP unidentified, 1,000 identified, 3,000 authenticated; over the limit returns 429.
- Use page[cursor] for harvesting beyond the first pages.

## Quirks
- publisher can be a string or an object with a name, depending on request parameters; handle both.
- disable-facets=true makes list requests lighter when you do not need facet counts.

## Terms and attribution
- DataCite metadata is CC0; the waiver does not apply to the linked datasets, which keep their own licences (see rightsList).
- DataCite asks for attribution and that records are not modified to make them false or misleading.
- Terms page: https://support.datacite.org/docs/harvesting-datacite-doi-metadata

## Sources
- https://support.datacite.org/docs/harvesting-datacite-doi-metadata: "All DataCite metadata is openly available with a CC0 license, meaning it is available to everyone."
- https://support.datacite.org/docs/rest-api: "The Public API only returns DOIs in Findable state, excluding draft records and Registered DOIs."
- https://support.datacite.org/docs/rate-limit: "If your request has been rate limited, the API will return a 429 response code."

## Help improve this skill
Missing a source, or see something to improve? Call the MCP tool suggest_source or feedback (or POST /api/suggest or /api/feedback). Reviewed ideas: https://signal-lab-jet.vercel.app/agent-suggestions

Information for educational purposes only. Free tiers, limits and licenses belong to the providers and may change; check the provider's own terms before using the data.
