Skip to main content

scholar-sidekick-python

Resolve scholarly identifiers (DOI including shortDOI aliases, PMID, PMCID, ISBN, arXiv, ISSN, ADS bibcode, WHO IRIS URL) into formatted citations (10,000+ CSL styles) and bibliography exports (BibTeX, RIS, EndNote, CSV…), verify a claimed citation against the record its identifier actually resolves to, and audit a reference list of any length for fabricated and retracted entries — from Python via the `scholar-sidekick` package. Wraps the public REST API; needs Python ≥3.9 but no API key for the free tier. Use when the agent runs Python (a script, a notebook, a data pipeline) and the user wants to cite, export, verify, or audit references by DOI, PMID, PMCID, ISBN, arXiv ID, ISSN, ADS bibcode, or WHO IRIS URL — especially a bibliography longer than 25 entries, which this client chunks and paces automatically.

Jump to install

Source facts

Repository
mlava/scholar-sidekick-skills
Last source activity
September 1, 2026 at 23:31
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
scholar-sidekick-python
description
Resolve scholarly identifiers (DOI including shortDOI aliases, PMID, PMCID, ISBN, arXiv, ISSN, ADS bibcode, WHO IRIS URL) into formatted citations (10,000+ CSL styles) and bibliography exports (BibTeX, RIS, EndNote, CSV…), verify a claimed citation against the record its identifier actually resolves to, and audit a reference list of any length for fabricated and retracted entries — from Python via the `scholar-sidekick` package. Wraps the public REST API; needs Python ≥3.9 but no API key for the free tier. Use when the agent runs Python (a script, a notebook, a data pipeline) and the user wants to cite, export, verify, or audit references by DOI, PMID, PMCID, ISBN, arXiv ID, ISSN, ADS bibcode, or WHO IRIS URL — especially a bibliography longer than 25 entries, which this client chunks and paces automatically.
version
1.0.0
author
Scholar Sidekick
license
MIT
metadata
{"tags":["citations","bibliography","doi","pmid","arxiv","csl","bibtex","ris","retraction","citation-verification","research","python","pandas","notebook","systematic-review"],"related_skills":["scholar-sidekick-api","scholar-sidekick-cli","scholar-sidekick-mcp"],"openclaw":{"emoji":"🐍","homepage":"https://scholar-sidekick.com","requires":{"bins":"[Truncated]"}}}
# Scholar Sidekick (Python) — Cite, verify and audit citations from Python Turn a scholarly identifier into a formatted citation, a bibliography file, or a fabrication check, using the `scholar-sidekick` package. It is a thin, typed client over the public Scholar Sidekick REST API. **No API key required** for the free, rate-limited tier. Results come back as objects, so you never parse text output. > Sibling skills, same API underneath: use **`scholar-sidekick-api`** when the agent only has > `curl`, **`scholar-sidekick-cli`** for a Node ≥20 terminal, or **`scholar-sidekick-mcp`** when an > MCP host is connected. This skill is the Python path. ## When to Use - The agent runs Python — a script, a notebook, or a data pipeline — and the user has an identifier (DOI, PMID, PMCID, ISBN, arXiv, ISSN, ADS bibcode, WHO IRIS URL; shortDOI aliases like `10/aabbe` accepted). - "Is this citation real / did you make it up?" — `verify()` compares the claimed title against the record the identifier resolves to. - "Check every reference in this list" — **especially a list longer than 25 entries.** `audit_bibliography()` chunks and paces itself; hand-rolled loops hit the rate limit. - The result feeds further Python work: a DataFrame, a report, a CI gate. - Do NOT use this to *search* for papers by topic. That is discovery; this assumes you already have an identifier. **This client does not cover everything the REST API does.** There is no standalone retraction check, no open-access check, no style search, and no bare identifier resolution. For those, use the `scholar-sidekick-api` or `scholar-sidekick-cli` skill. Retraction screening *is* available, but only inside an audit. ## Install Published on PyPI as `scholar-sidekick` (import name `scholar_sidekick`). Requires Python ≥ 3.9. ```bash pip install scholar-sidekick pip install 'scholar-sidekick[pandas]' # adds report.to_dataframe() ``` ## API surface | Method | Purpose | Returns | |---|---|---| | `format()` | Resolve identifiers and format them | `FormatResult` (`.text`, `.html`) | | `format_items()` | Format already-resolved CSL-JSON items | `FormatItemsResult` (`.output`) | | `export()` | Export to a bibliography file format | `str` — the file body | | `verify()` | Check one claimed citation against the resolved record | `VerifyResult` | | `audit()` | Audit up to 25 references in one call | `AuditReport` | | `audit_bibliography()` | Audit any number, chunked and paced | `AuditReport` | | `health()` | Service liveness | `dict` | `AsyncScholarSidekick` mirrors every method; only the awaiting differs. ## Procedure ### Verify one citation (catch fabrication) ```python from scholar_sidekick import ScholarSidekick client = ScholarSidekick() result = client.verify( title="Quantum entanglement in biological systems", doi="10.1038/nphys1170", ) print(result.verdict, result.confidence) ``` `title` is required, plus at least one identifier keyword (`doi`, `pmid`, `pmcid`, `isbn`, `arxiv`, `issn`, `ads`, `who_iris_url`). Optional: `authors`, `year`, `container`. | verdict | meaning | |---|---| | `matched` | the claimed citation agrees with the resolved record | | `mismatch` | the identifier resolves to a **different** work — the dominant fabrication pattern (real DOI, invented title; Topaz et al., Lancet 2026) | | `ambiguous` | a discrepancy a human should read; not an accusation | | `not_found` | no record found in the registries searched | Two rules that matter: - A `mismatch` is **returned as a value, never raised.** It is the expected outcome for a fabricated citation, and the reason this method exists. - `not_found` is **not** proof of fabrication. Standards documents, software repositories, model cards and institutional reports are often real but absent from scholarly registries. Report it as "could not confirm". Do not use `format()` to answer "is this real?". A fabricated citation carries a real, resolvable DOI, so formatting it succeeds and proves nothing. Only `verify()` compares the claimed title. ### Audit a whole bibliography ```python claims = [ {"title": "Attention is all you need", "arxiv": "1706.03762"}, {"title": "Ileal-lymphoid-nodular hyperplasia…", "doi": "10.1016/S0140-6736(97)11096-0"}, # …any number of entries ] report = client.audit_bibliography( claims, progress=lambda done, total: print(f"{done}/{total} chunks"), ) print(report.summary) # total / matched / mismatch / ambiguous / not_found / errored / retracted for entry in report.needs_review: print(entry.input_index, entry.verdict, entry.claimed_title, entry.resolved_title) ``` - **`entry.input_index` is the 0-based position in the list you passed in**, whatever the chunking did. Use it to point at the right reference. - Each entry also carries `retracted`, `has_concern`, `has_corrections`, and `notices` — retraction screening is **on by default**. Pass `checks=[]` to skip it. `"retraction"` is the only accepted check value. - A failed chunk is recorded, not raised, so one transient upstream error does not discard several minutes of completed work: ```python if not report.complete: for failure in report.errors: print(f"entries {failure.start_index}-{failure.end_index} failed: {failure.error}") ``` Pass `stop_on_error=True` to raise on the first failed chunk instead. **Claim dicts are strict — the server rejects unknown keys.** Allowed: `title`, `authors` (`[{"family": …, "given": …}]`, max 50), `year` (int), `container`, and the identifiers `doi`, `pmid`, `pmcid`, `isbn`, `arxiv`, `issn`, `ads`, `whoIrisUrl` (note the camelCase). Strip anything else before sending. **Two input kinds, and the wrong one fails.** Default `kind="claims"` expects dicts. For raw prose reference strings — a pasted reference list with no parsed title — pass `kind="references"`, which verifies by containment instead of title comparison: ```python report = client.audit_bibliography(reference_strings, kind="references") ``` With pandas installed: `report.to_dataframe()`. Use `audit()` instead of `audit_bibliography()` only when you have 25 entries or fewer *and* want a single call. `audit()` also accepts a raw `bibliography=` string (BibTeX / RIS / CSL-JSON) which the server parses; pass `format=` to override auto-detection. This audits citation **identity**. It does not check whether a source supports the claim it is cited for. ### Format and export ```python print(client.format(text="10.1038/nphys1170", style="vancouver").text) with open("refs.bib", "w") as fh: fh.write(client.export(text="10.1038/nphys1170 PMID:30049270", format="bibtex")) ``` - `format()` takes exactly one of `text=` (a blob the API detects identifiers in) or `lines=` (a sequence). Passing both, or neither, raises `ValueError`. - `style`: `vancouver` (default), `ama`, `apa`, `ieee`, `cse`, or any CSL style ID (`chicago-author-date`, `nature`, `the-lancet`, …). - `export()` formats: `bibtex`, `ris`, `csv`, `csl`, `endnote-xml`, `endnote-refer`, `refworks`, `medline`, `zotero-rdf`, `txt`. It returns the file body as a string — write it yourself. ## Errors Exceptions are chosen by the API's stable error `code`, not by HTTP status. ```python 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 data source failed after the API's own retries | | `TransportError` | connection failure or timeout; no HTTP response | Every `APIError` carries `.request_id`. Include it when reporting a problem. **On any exception, report the failure.** Never invent a citation, a retraction status, or a `matched` verdict the client did not return. ## Authentication and limits Works **anonymously** at the free, rate-limited tier — fine for normal agent use. A key raises the limit about fivefold but is never required: ```python client = ScholarSidekick(api_key="ssk_…") ``` Precedence: the explicit argument, then `SCHOLAR_SIDEKICK_API_KEY`, then anonymous. Free keys come from <https://scholar-sidekick.com/account>. Other constructor options: `base_url`, `timeout` (30 s), `connect_timeout` (10 s), `max_retries` (2). `SCHOLAR_SIDEKICK_BASE_URL` overrides the host. Both clients close cleanly as context managers: ```python with ScholarSidekick() as client: ... async with AsyncScholarSidekick() as client: result = await client.verify(title="…", doi="10.1038/nphys1170") ``` ## Pitfalls - **No `resolve()`, no retraction method, no open-access method, no style search.** Only the seven methods above exist. Use the `scholar-sidekick-api` or `-cli` skill for the rest. - `format()` returns **whole-batch** `.text`/`.html` covering every input, newline-joined — not one string per item. Do not split it to synthesise per-item citations; styles put newlines inside entries. `format_items()` names the same string `.output`, deliberately. - `AuditEntry` has **`claimed_title` and `resolved_title`**, not `.title`. - `kind="claims"` needs dicts; raw strings need `kind="references"`. - `audit()` requires exactly one of `bibliography=`, `claims=`, or `references=` — it raises `ValueError` locally rather than spending a round trip. - `audit_bibliography()` sends chunks **serially even in the async client**. That is deliberate: the constraint is the server's upstream budget, not local concurrency. Do not parallelise it. - Pass identifiers verbatim. `PMID:`, `arXiv:`, ISBN hyphens and `https://doi.org/…` are all tolerated. ## Verification ```python from scholar_sidekick import ScholarSidekick client = ScholarSidekick() assert client.health().get("ok") is True # A real DOI with a deliberately wrong title must come back `mismatch`. print(client.verify(title="A title this paper does not have", doi="10.1038/nphys1170").verdict) ```
View on GitHub