Resolve, verify, format and audit scholarly citations — a thin Python client for the Scholar Sidekick API.
Project description
scholar-sidekick
Resolve, verify, format and audit scholarly citations from Python.
A thin client for the Scholar Sidekick REST API — citation formatting in 10,000+ CSL styles, bibliography export, retraction and open-access checks, and verification that a citation is real rather than fabricated.
pip install scholar-sidekick
Quick start
from scholar_sidekick import ScholarSidekick
client = ScholarSidekick()
# Format an identifier
print(client.format(text="10.1038/nphys1170", style="vancouver").text)
# Export a bibliography
print(client.export(text="10.1038/nphys1170", format="bibtex"))
Verifying citations
The dominant AI-fabrication pattern is a real, resolvable DOI paired with an invented title.
Such citations resolve cleanly under doi.org, so "follow the link and see if it loads" does not
catch them. Comparing the claimed title against the resolved record does.
result = client.verify(
title="Quantum entanglement in biological systems",
doi="10.1038/nphys1170",
)
print(result.verdict) # matched | mismatch | ambiguous | not_found
print(result.confidence) # high | medium | low
A mismatch is returned as a value, never raised as an exception — it is the expected outcome for
a fabricated citation, and the reason this method exists.
Interpreting verdicts
| verdict | meaning |
|---|---|
matched |
the claimed citation agrees with the resolved record |
mismatch |
the identifier resolves to a different work — likely fabrication |
ambiguous |
a discrepancy that needs human eyes, not an accusation |
not_found |
no record found in the registries searched |
not_found is not proof of fabrication. Standards documents, software repositories, model
cards and institutional reports are frequently real but absent from the scholarly registries the
API searches. Treat it as "could not confirm", not "invented".
Auditing a whole bibliography
The API audits up to 25 references per call and rate-limits those calls. audit_bibliography()
handles both for you: it chunks, paces itself from the server's rate-limit headers, and sends
chunks serially — the server already fans out internally, so parallelising here would compete
with it for the same upstream budget.
report = client.audit_bibliography(
references, progress=lambda done, total: print(f"{done}/{total}")
)
print(report.summary) # verdict counts across every chunk
print(len(report.needs_review)) # entries a human should look at
for entry in report.needs_review:
print(entry.input_index, entry.verdict, entry.title)
entry.input_index is the 0-based position in the list you passed in, so it always points at the
right reference regardless of how the work was chunked.
A chunk that fails is recorded rather than raised, so one transient upstream error does not discard several minutes of completed work:
if not report.complete:
for failure in report.errors:
print(f"entries {failure.start_index}-{failure.end_index} failed: {failure.error}")
With pandas installed (pip install 'scholar-sidekick[pandas]'):
df = report.to_dataframe()
df[df.verdict != "matched"]
There is a runnable end-to-end example in
examples/systematic_review_audit.ipynb.
Authentication
Every endpoint works anonymously — no key, no signup. An API key raises the rate limit (about fivefold on the free tier) but is never required.
client = ScholarSidekick(api_key="ssk_…")
Or set SCHOLAR_SIDEKICK_API_KEY in the environment. Precedence is the explicit argument, then the
environment variable, then anonymous. Free keys are issued at
scholar-sidekick.com/account.
Async
Every method has an async equivalent:
from scholar_sidekick import AsyncScholarSidekick
async with AsyncScholarSidekick() as client:
result = await client.verify(title="…", doi="10.1038/nphys1170")
Note that audit_bibliography() still issues its chunks serially in the async client — the
constraint is the server's upstream budget, not this client's concurrency.
Errors
Exceptions are selected by the API's stable error code, not by HTTP status (the contract
guarantees the code; statuses may change).
from scholar_sidekick import APIError, RateLimitError, UpstreamError
try:
client.format(text="10.1038/nphys1170")
except RateLimitError as exc:
print("retry after", exc.retry_after)
except UpstreamError as exc:
print("a data source failed:", exc.code)
except APIError as exc:
print(exc.code, exc.message, exc.request_id)
| exception | when |
|---|---|
ValidationError |
the request was malformed or invalid |
AuthError |
authentication or entitlement was refused |
RateLimitError |
rate limit exceeded; carries .retry_after |
UpstreamError |
a scholarly data source failed after the API's own retries |
TransportError |
connection failure or timeout; no HTTP response |
Every APIError carries .request_id — include it in bug reports.
API surface
| method | purpose |
|---|---|
format() |
resolve identifiers and format them |
format_items() |
format already-resolved CSL-JSON items |
export() |
export to RIS, BibTeX, CSV, EndNote XML, … |
verify() |
check one citation against the resolved record |
audit() |
audit up to 25 references in one call |
audit_bibliography() |
audit any number, chunked and paced |
health() |
service liveness |
format() returns whole-batch text/html strings covering every input, newline-joined — the API
has no per-item formatted field, and this client does not invent one by splitting that string.
format_items() names the same concept output. The difference is preserved deliberately rather
than papered over.
Related
- Scholar Sidekick API docs
scholar-sidekick-cli— the same API from the terminalscholar-sidekick-mcp— MCP server for AI assistants
Development
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,pandas]"
pytest --cov
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scholar_sidekick-0.1.0.tar.gz.
File metadata
- Download URL: scholar_sidekick-0.1.0.tar.gz
- Upload date:
- Size: 44.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f205942c7ad647bf859b1cd1121a5e6fd0d40e46ad0b14baae5e7936c8fd811f
|
|
| MD5 |
b94dc1f360b1384cd0269889d647dad5
|
|
| BLAKE2b-256 |
31899cd28ad25556c75715a84fdbf0666b928ed449fa1a0e6783b3a056380613
|
Provenance
The following attestation bundles were made for scholar_sidekick-0.1.0.tar.gz:
Publisher:
publish.yml on mlava/scholar-sidekick-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scholar_sidekick-0.1.0.tar.gz -
Subject digest:
f205942c7ad647bf859b1cd1121a5e6fd0d40e46ad0b14baae5e7936c8fd811f - Sigstore transparency entry: 2341581479
- Sigstore integration time:
-
Permalink:
mlava/scholar-sidekick-python@f955e8081baf90a9aaf3959978fb315c3b91f908 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mlava
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f955e8081baf90a9aaf3959978fb315c3b91f908 -
Trigger Event:
release
-
Statement type:
File details
Details for the file scholar_sidekick-0.1.0-py3-none-any.whl.
File metadata
- Download URL: scholar_sidekick-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58f6d552d934faabb790e2f504288d8d6cdf23ca57b057d2cfca447eecf58f98
|
|
| MD5 |
2092b3c895cd1f71fc6cb8f725b0a2e2
|
|
| BLAKE2b-256 |
458dadb1bd5ac733aa4b74bd1ba05a833de29823274fe9661bc13533010b118b
|
Provenance
The following attestation bundles were made for scholar_sidekick-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on mlava/scholar-sidekick-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scholar_sidekick-0.1.0-py3-none-any.whl -
Subject digest:
58f6d552d934faabb790e2f504288d8d6cdf23ca57b057d2cfca447eecf58f98 - Sigstore transparency entry: 2341581484
- Sigstore integration time:
-
Permalink:
mlava/scholar-sidekick-python@f955e8081baf90a9aaf3959978fb315c3b91f908 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mlava
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f955e8081baf90a9aaf3959978fb315c3b91f908 -
Trigger Event:
release
-
Statement type: