API
The same evidence the pages show — for your library system, your submission system, your own dashboard. JSON, CSV or JSON Lines; a change feed; webhooks; badges and cards for any page.
Your key, in a header
Every request carries one of your organisation's keys in a header: X-Api-Key: ssk_… (or Authorization: Bearer ssk_…). Never put a key in the address — an address is written into server logs, proxies and browser histories — and the API refuses a key sent that way. Make, name, rotate and revoke keys on API keys: a key is shown once when it is made, and we keep only its fingerprint (SHA-256). Call the API from your server, not from a web page: a key in a page's script is visible to every visitor. For pages, use the badge and the card below — they need no key.
curl -H "X-Api-Key: ssk_…" "https://partners.smartscholars.in/api.php?action=rate&q=2456-5660"
Limits
Requests an hour, for all your organisation's keys together, by plan:
| Plan | Requests an hour |
|---|---|
| No plan (every key) | 600 |
| Integrity OS — college | 1,200 |
| Integrity OS — university | 3,000 |
| Integrity OS — multi-campus | 6,000 |
| Risk Radar — institution | 3,000 |
| Risk Radar — enterprise | 10,000 |
Every answer carries three headers: X-RateLimit-Limit (your limit for the hour), X-RateLimit-Remaining (what is left of it) and X-RateLimit-Reset (seconds until the count starts again). Over the limit the answer is HTTP 429 with Retry-After, in seconds. The hour runs from :00 to :59, India time. A rate_bulk call of 100 journals counts as one request. Plans: Plans.
Every action
One address: https://partners.smartscholars.in/api.php, with action= naming what you want. Answers are JSON with "ok": true; an error has "ok": false, an error in words, a help link to the fix, and an HTTP status (400, 401, 402, 404, 429).
rateJSONevery key
One journal's Journal Trust Rating: grade, score, the five pillars and every check with its source, the methodology it was made under and, when its latest reading failed, latest_reading_failed (as in rate_bulk). The stored rating is given while it is under 7 days old; refresh=1 re-checks it now (about 10 seconds).
GET ?action=rate&q=2456-5660 GET ?action=rate&q=https://journal.example.org&refresh=1
rate_bulkJSON, CSV, JSON Linesevery key; with a plan that includes the risk API, a journal with no Risk Radar score is scored too
Up to 100 journals in one call, from the ratings already stored: grade, score, rated date, the methodology it was made under, Risk Radar band, evidence links. A journal never rated, or rated more than 7 days ago, comes back "status": "queued" and is rated within minutes — ask again for it. A journal no registry knows gets grade U, insufficient data. When a journal's latest reading could not reach enough sources, latest_reading_failed says when, in the words of its rating page; the grade is then its last complete rating (U if it has none), and it is read again after 24 hours — until then it comes back rated if that rating is under 7 days old, failed if not. A journal that could not be rated in three tries also comes back failed (tried again after a day); a mistyped ISSN invalid. Each journal's risk.status is scored, stale (older than 7 days), queued, not_scored (scoring on request needs the risk API) or failed (no registry gave a record to score).
GET ?action=rate_bulk&issn=2456-5660,…
POST ?action=rate_bulk with the JSON body {"issns": ["2456-5660", …]}changesJSON, CSV, JSON Linesevery key
The change feed: every grade change, every Risk Radar band change and every new hijack or clone signal on your journals since a date (see below).
GET ?action=changes&since=2026-10-01T00:00:00Z GET ?action=changes&since=2026-10-01&until=2026-10-08
verifyJSONevery key
Up to 100 DOIs: registered at Crossref, journal, title, authors, year, whether the DOI link resolves, retraction and correction notices, the journal's rating.
POST action=verify&dois=10.1000/a,10.1000/b
portfolioJSONevery key
Every journal on a partner account, with its rating, audit score, last DOI check, monitor score, archived articles and index applications.
GET ?action=portfolio
journalJSONevery key
One rating record with its history (the last 24 checks).
GET ?action=journal&id=123
riskJSONa plan with the risk API: Integrity OS university or multi-campus, or a Risk Radar plan
The Journal Risk Radar score 0–100, its band, the fifteen signals with rule, detail and source, and the history. refresh=1 re-scores.
GET ?action=risk&issn=2456-5660
screenJSONa plan with API access
Manuscript Screen: the AI-writing, paper-mill and reference signals of a text of 80 words or more.
POST action=screen&text=…&refs=1
Formats
format=json (the default) · format=csv — a spreadsheet, one row a journal or a change, UTF-8, a header row first · format=jsonl — JSON Lines, one JSON object a line. rate_bulk and changes answer in all three; the other actions in JSON. Times in rate_bulk and changes are ISO-8601 in UTC (they end in Z).
curl -H "X-Api-Key: ssk_…" -H "Content-Type: application/json" \
-d '{"issns": ["2456-5660", "…"]}' "https://partners.smartscholars.in/api.php?action=rate_bulk&format=csv"{ "ok": true, "count": 1, "rated": 1, "queued": 0, "failed": 0, "invalid": 0,
"journals": [ { "issn": "2456-5660", "status": "rated", "grade": "B", "score": 74, "rated_at": "2026-10-03T04:30:00Z",
"methodology": "…", "latest_reading_failed": null, "risk": { "status": "scored", "band": "guarded", "score": 24, … },
"evidence": { "rating": "https://scholar.smartscholars.in/check.php?q=2456-5660", "risk": "…", "badge": "…", "card": "…", "sources": [ … ] } } ] }The change feed
action=changes&since=… — an ISO-8601 date or date-time (2026-10-01, 2026-10-01T06:30:00Z, or with an offset — send + as %2B; a time without a zone is India time). It lists, oldest first, every change on your journals — your watch-list, your Risk Radar watch-list, the journals your faculty vault touches and, for a partner, its portfolio:
grade.changed— the Journal Trust Rating grade moved from one grade (A to E) to another, against the grade before it: from, to, the scores and the methodology the new grade was made under. A reading that could not reach enough sources (U, insufficient data) is not a grade and changes nothing; the first grade after only such readings comes with"from": null;risk.band_changed— the Risk Radar band moved, withdirectionraised, lowered or changed (to or from "not enough data");risk.hijack_signal— a hijack or clone signal (the Radar's rules identity, mimicry, listed) that the score before did not have. From 9 Oct 2026 each new score is compared with the one before it; a signal already present on 9 Oct 2026 is listed once, with"already_present": true.
Each change has an id, a time, the journal and the public page with its evidence. Ask again with since= the until of the last answer; a change on that very second comes in both answers — keep the id to ignore it. At most 1,000 changes an answer: when "truncated": true, ask again from next_since.
Webhooks
A webhook — an address of yours we call when something changes, so your system hears it without asking. Add one on Webhooks: an https address, the events you want, and a secret shown once. The events: grade.changed (a journal you watch changed its Journal Trust Rating grade); risk.band_raised (a journal you watch moved up a Risk Radar band); risk.hijack_signal (a hijack or clone signal appeared on a journal you watch); faculty.retraction (a retraction notice appeared on a publication in your faculty vault).
Each call is a POST of a JSON body — event, id (one per delivery: keep it to ignore a repeat), sent_at (UTC), then the event's own fields — with the headers X-SS-Event, X-SS-Delivery and X-SS-Signature: sha256=<HMAC-SHA256 of the raw body, keyed with your secret>. Answer with any 2xx within 5 seconds. Redirects are not followed. A call that fails is tried again about 5 and then 30 minutes later — three tries in all; after 20 failed calls in a row the address is paused and you are told, with a button to re-enable it. Missed something? The change feed has it.
{ "event": "grade.changed", "id": "whd_…", "sent_at": "2026-10-09T00:30:00Z",
"journal": { "issn": "2456-5660", "title": "…", "url": "…" }, "from": "B", "to": "C",
"rated_at": "…", "methodology": "…", "evidence_url": "https://scholar.smartscholars.in/check.php?q=2456-5660" }Check the signature
Before you trust a call, compute the HMAC-SHA256 of the raw body — the bytes exactly as they arrived, before any JSON decoding — with your secret, and compare it with X-SS-Signature in constant time. These two pieces are tested against our own deliveries on every release.
PHP
<?php
// Smart Scholars webhook receiver: check the signature before trusting the call.
$secret = getenv('SS_WEBHOOK_SECRET'); // the secret shown once when you added this address (whsec_...)
$body = file_get_contents('php://input'); // the raw body, exactly as it arrived
$sent = $_SERVER['HTTP_X_SS_SIGNATURE'] ?? '';
$good = 'sha256=' . hash_hmac('sha256', $body, (string) $secret);
if (!$secret || !hash_equals($good, $sent)) {
http_response_code(401);
exit('signature does not match');
}
$event = json_decode($body, true); // event, id, sent_at, then the event's own fields
// ... act on $event['event'] here; keep $event['id'] to ignore a repeat ...
http_response_code(200);
echo 'ok';Python
import hashlib
import hmac
def is_from_smart_scholars(secret, raw_body, signature_header):
"""secret: your whsec_... secret; raw_body: the request body exactly as it arrived, as bytes
(Flask: request.get_data(), Django: request.body); signature_header: X-SS-Signature."""
good = "sha256=" + hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(good, signature_header or "")Badges and cards — no key
For a web page, never the API: the Journal Trust Rating badge https://scholar.smartscholars.in/badge.php?issn=2456-5660 (an SVG image) and the card https://scholar.smartscholars.in/embed.php?issn=2456-5660 (for an iframe) show the public facts only — the grade, the date rated, a link to every check. The Risk Radar badge is https://scholar.smartscholars.in/radar.php?issn=2456-5660&badge=1; with the Risk Radar enterprise plan add &for= and your organisation's public badge id (on API keys) for the white-label badge. A badge never takes your API key: a key put in a badge's address is not used, and your dashboard warns you to rotate it.
Start in three steps
Example answer (rate)
{
"ok": true,
"journal": {
"title": "Journal of Science and Technology",
"issn": "2456-5660", "grade": "C", "score": 62,
"grade_label": "Developing — real gaps an indexer will notice",
"pillars": { "identity": {"pts": 21, "max": 25, "pct": 84}, … },
"checks": [
{ "pillar": "technical", "key": "crossref", "label": "Crossref member",
"status": "ok", "points": 4, "max": 4,
"detail": "490 DOIs registered, prefix 10.46243",
"source": "https://api.crossref.org/journals/2456-5660",
"fix": "" }, …
],
"evidence_page": "https://scholar.smartscholars.in/check.php?q=2456-5660"
}
}Terms
Ratings are computed from public registries and the journal's own site and may change weekly; always link to the evidence page when you show a grade to a third party. Do not resell raw API output. Keys are per organisation: rotate a leaked key yourself, at once, on API keys.


