Skip to content

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; on 429, back off and retry
  • Store the API Key in the plugin's preferences, not hardcoded into the released version
  • is_warning alone is enough for red flagging; read sources[].code when 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 Key
python
# 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)

PaperPop Home