Integration Examples
curl
bash
# Impact factor
curl -X POST https://api.byteslink.cn/journals/dev/impact-factors/ \
-H "X-API-Key: pp_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"journals": ["J Med Chem", "CLINICAL SOCIAL WORK JOURNAL"]}'
# Warning / predatory journal check
curl -X POST https://api.byteslink.cn/journals/dev/warnings/ \
-H "X-API-Key: pp_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"journals": ["CONNECTION SCIENCE"]}'Python
python
import requests
BASE = "https://api.byteslink.cn/journals"
HEADERS = {"X-API-Key": "pp_xxxxxxxx"}
def query(endpoint: str, journals: list[str]) -> list[dict]:
"""Batch query, automatically split into batches of 20 (each batch billed once)"""
results = []
for i in range(0, len(journals), 20):
r = requests.post(f"{BASE}/dev/{endpoint}/",
json={"journals": journals[i:i+20]},
headers=HEADERS, timeout=10)
if r.status_code == 402:
raise RuntimeError("Insufficient quota, please top up")
r.raise_for_status()
results += r.json()["results"]
return results
ifs = query("impact-factors", ["J Med Chem", "CLINICAL SOCIAL WORK JOURNAL"])
warns = query("warnings", ["CONNECTION SCIENCE"])
print(ifs[0]["impact_factor"], warns[0]["is_warning"])
# The specific lists matched (warning list / predatory journals)
for w in warns:
if w["is_warning"]:
print(w["query"], "matched:", [s["name_en"] for s in w["sources"]])Zotero Plugin
Typical requirement: show impact factor/quartile in the item list, and flag warning / predatory journals in red.
javascript
const BASE = "https://api.byteslink.cn/journals";
const API_KEY = "pp_xxxxxxxx"; // read from plugin preferences
async function fetchJournalData(endpoint, journals) {
const resp = await Zotero.HTTP.request("POST", `${BASE}/dev/${endpoint}/`, {
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ journals }),
responseType: "json",
});
return resp.response.results;
}
// Get journal names of currently selected items (deduplicated, up to 20 per batch)
function selectedJournalNames() {
const items = Zotero.getActiveZoteroPane().getSelectedItems();
const names = items
.map((it) => it.getField("publicationTitle"))
.filter(Boolean);
return [...new Set(names)].slice(0, 20);
}
async function annotateItems() {
const names = selectedJournalNames();
if (!names.length) return;
// Request both endpoints concurrently (each billed separately)
const [ifs, warns] = await Promise.all([
fetchJournalData("impact-factors", names),
fetchJournalData("warnings", names),
]);
const ifMap = new Map(ifs.map((r) => [r.query, r.impact_factor]));
// A journal may match multiple sources; keep their names for tooltips
const warnMap = new Map(
warns
.filter((r) => r.is_warning)
.map((r) => [r.query, r.sources.map((s) => s.name_en).join(", ")])
);
for (const name of names) {
const f = ifMap.get(name);
const label = f ? `IF ${f.JIF} | JCR Q${f.Q} | CAS Tier ${f.N}` : "Not indexed";
const danger = warnMap.has(name) ? `(${warnMap.get(name)}, recommend flagging in red)` : "";
Zotero.debug(`${name}: ${label} ${danger}`);
// In an actual plugin: write to a custom column / extra field, style matched rows red,
// and show sources' name / desc in a tooltip
}
}Implementation suggestions:
- Cache results locally by journal name (IF data updates yearly, the lists update infrequently) to reduce quota usage
- On receiving
402, guide the user to top up in the User Center; on429, back off and retry - Store the API Key in the plugin's preferences, not hardcoded into the released version
is_warningalone is enough for red flagging; readsources[].codewhen you need to distinguish the CAS warning list from predatory journals
OpenClaw Skill
The Skill only needs to wrap a single "journal safety check + metric lookup" action:
yaml
# skill config example
name: journal-check
description: Query a journal's impact factor/quartile and check whether it is on the CAS International Journal Early Warning List or the predatory journal list
env:
JOURNAL_API_KEY: the user's API Keypython
# skill execution logic example
import os, requests
def journal_check(names: list[str]) -> str:
base = "https://api.byteslink.cn/journals"
headers = {"X-API-Key": os.environ["JOURNAL_API_KEY"]}
body = {"journals": names[:20]}
ifs = requests.post(f"{base}/dev/impact-factors/", json=body,
headers=headers, timeout=10).json()["results"]
warns = requests.post(f"{base}/dev/warnings/", json=body,
headers=headers, timeout=10).json()["results"]
lines = []
for a, b in zip(ifs, warns):
if a["found"]:
f = a["impact_factor"]
metric = f"IF={f['JIF']}, JCR Q{f['Q']}, CAS Tier {f['N']}"
else:
metric = "Not indexed in JCR"
if b["is_warning"]:
hit = ", ".join(s["name_en"] for s in b.get("sources", []))
flag = f"⚠️ Matched: {hit}"
else:
flag = "✅ Not on the warning / predatory lists"
lines.append(f"{a['query']}: {metric} | {flag}")
return "\n".join(lines)