AiCitationChecker REST API — POST /api/verify and supporting endpoints.
All API requests require a Bearer token in the Authorization header.
POST /api/token
Content-Type: application/json
{"email": "your@email.com", "password": "your-password"}
Response: {"api_key": "…43-char token…", "username": "…", "tier": "…"}
POST /api/generate_key Authorization: Bearer <current-key>
The old key is immediately invalidated.
Pass the key in every subsequent request:
Authorization: Bearer <api-key>
Verify a batch of bibliographic references against CrossRef, Semantic Scholar, and OpenAlex.
{
"references": [
"Smith, J.; Jones, A. Title of paper. Journal 2020, 10, 123.",
"Doe, J. Another title. Journal 2021, 5, 456-470, https://doi.org/10.xxxx/yyyy."
],
"mode": "all",
"citation_style": "MDPI"
}
| Field | Type | Required | Options |
|---|---|---|---|
references | array of strings | yes | One reference per string |
mode | string | no (default all) | all · doi_only · no_doi_only |
citation_style | string | no (default APA) | APA · MDPI · Chicago · Harvard · IEEE · Vancouver |
Mode values:
all — verify every reference regardless of whether a DOI is presentdoi_only — only process references that contain a DOIno_doi_only — only process references without a DOI{
"credits_used": 10.0,
"credits_remaining": 98432.0,
"results": [
{
"index": 0,
"input": "Smith, J.; Jones, A. Title of paper. Journal 2020, 10, 123.",
"status": "OK",
"confidence": "EXCELLENT",
"doi": "10.xxxx/yyyy",
"matched_title": "Title of paper",
"formatted_citation": "Smith, J.; Jones, A. Title of paper. Journal 2020, 10, 123. https://doi.org/10.xxxx/yyyy",
"page_mismatch": false,
"pages_ref": null,
"pages_crossref": "123-131"
}
]
}
index corresponds to the position in the input references array (0-based).
page_mismatch is true when the page range extracted from the source differs from CrossRef — even for OK results. When true, use pages_crossref (the value in formatted_citation) as the authoritative page range.
| Status | Meaning |
|---|---|
OK | Reference verified — DOI resolves and author/title match CrossRef. Page numbers are not checked by the status; always inspect page_mismatch and formatted_citation. |
E1 | DOI cannot be resolved. Common causes: truncated DOI (journal-prefix only), DOI not indexed in CrossRef (some regional/Persian journals), or broken resolver. When a title is available the API attempts a title-based fallback and includes a suggestion. |
E2 | DOI resolves but first author differs significantly — strong hallucination indicator. |
E3 | Year mismatch >1 year. Preprint vs. final publication is tolerated (±1 year). |
E4 | Title similarity below 70%. May also occur when a DOI resolves to a journal landing page rather than a specific article. |
WRONG_DOI | The correct article was found by title search, but the DOI in the source points to a different article. Can be a false positive — verify manually before changing the DOI. |
WRONG_AUTHOR | Title matches a real paper but authors do not — hallucination indicator. |
WRONG_JOURNAL | Title and authors match but the journal differs. |
SUGGESTION | Possible match found for a reference without a DOI. Confidence level (HIGH/MEDIUM/LOW) indicates reliability. LOW confidence for web/government sources usually means the search misfired — ignore it. |
SUGGESTION_S2 | Match found via Semantic Scholar (broader coverage including arXiv and conference papers). Verify manually. |
NO_DOI | No DOI found and no match in databases. For web, institutional, or legislative references this is expected and correct. |
| Confidence | Meaning |
|---|---|
EXCELLENT | ≥95% title similarity — high certainty |
GOOD | 85–95% — solid match with minor uncertainty |
ACCEPTABLE | 70–85% — review the formatted citation carefully |
WEAK | 50–70% — treat with caution; verify manually |
CRITICAL | <50% — requires manual review regardless of status |
NOT_FOUND | No match at all |
| Combination | Recommended action |
|---|---|
OK + EXCELLENT | Accept as-is; also check page_mismatch |
OK + GOOD | Accept; spot-check if the reference is critical |
E1 + suggestion present | DOI is broken/truncated but article was found by title — verify suggestion then correct the DOI |
E4 + CRITICAL | Check both the DOI and the formatted citation carefully |
WRONG_DOI + GOOD | Verify manually — may be a false positive |
SUGGESTION + LOW | Ignore if source has no DOI (misfired); verify manually otherwise |
The status reflects DOI resolution and title match — page numbers are not validated. When the source page range differs from CrossRef the response includes "page_mismatch": true along with pages_ref and pages_crossref. Always apply the page range from formatted_citation (which reflects CrossRef data).
| Cause | Example |
|---|---|
| Truncated DOI (journal prefix only) | 10.1680/ensu. instead of 10.1680/ensu.14.00017 |
| DOI valid but not indexed in CrossRef | Some Persian journals, IDOSI journals, regional publishers |
| DOI resolver link broken | Journal hosted on an alternative server |
Some publishers assign a DOI to the journal itself (e.g. the journal prefix without an article suffix). The API detects this case and returns E1 with a note, rather than E4. Check whether the PDF of the article actually contains a DOI before trusting the source.
WRONG_DOI with GOOD confidence can be a false positive. Always verify manually before changing the DOI in the document.
When a reference contains et al., the API expands the full author list in formatted_citation by looking up the DOI in CrossRef. This works reliably and is particularly useful for MDPI compliance.
The API handles mixed input styles. If a reference is in APA style but contains a valid DOI, the output is correctly reformatted to the requested citation style regardless of the input format. Author separators, initials, and journal capitalisation are all normalised.
If a reference was cited from an early-access version, the API may return final published metadata with an updated year, volume, and page range. Always apply these corrections from formatted_citation.
NO_DOI for these sources is expected and correct, not an error. If such a reference returns SUGGESTION with LOW confidence, the search misfired — ignore the result.
ASCE journal DOIs use the format 10.1061/(ASCE)…:vol(startpage). The API derives the full page range from CrossRef automatically, even when the source only lists volume and issue number.
curl -s -X POST https://aicitationchecker.org/api/verify \
-H "Authorization: Bearer <key>" \
-H "Content-Type: application/json" \
-d '{
"references": [
"Mealy, P.; Teytelboym, A. Economic complexity and the green economy. Res. Policy 2022, 51, 103948, https://doi.org/10.1016/j.respol.2020.103948."
],
"mode": "all",
"citation_style": "MDPI"
}'
import json, urllib.request
KEY = "your-api-key-here"
REFS = [
"Mealy, P.; Teytelboym, A. Economic complexity and the green economy. "
"Res. Policy 2022, 51, 103948, https://doi.org/10.1016/j.respol.2020.103948.",
]
payload = json.dumps({
"references": REFS,
"mode": "all",
"citation_style": "MDPI"
}).encode("utf-8")
req = urllib.request.Request(
"https://aicitationchecker.org/api/verify",
data=payload,
headers={
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json"
}
)
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read().decode("utf-8"))
print(f"Credits used: {data['credits_used']} Remaining: {data['credits_remaining']}")
for r in data["results"]:
pm = " ⚠ page mismatch" if r.get("page_mismatch") else ""
print(f"[{r['index']+1}] {r['status']:12s} {r.get('confidence',''):10s} DOI: {r.get('doi','—')}{pm}")
import json, urllib.request
from docx import Document
DOCX = "path/to/paper.docx"
KEY = "your-api-key-here"
BATCH = 30 # max per tier (see Tier Limits below)
doc = Document(DOCX)
in_refs = False
refs = []
for para in doc.paragraphs:
t = para.text.strip()
if not in_refs:
if t.lower() == "references":
in_refs = True
continue
if t.lower().startswith(("disclaimer", "appendix", "supplementary")):
break
if t:
refs.append(t)
for i in range(0, len(refs), BATCH):
batch = refs[i:i+BATCH]
payload = json.dumps({
"references": batch,
"mode": "all",
"citation_style": "MDPI"
}).encode("utf-8")
req = urllib.request.Request(
"https://aicitationchecker.org/api/verify",
data=payload,
headers={"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
)
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read().decode("utf-8"))
for r in data["results"]:
global_idx = i + r["index"] + 1
pm = " ⚠ PAGE MISMATCH" if r.get("page_mismatch") else ""
print(f"[{global_idx:03d}] {r['status']:12s} {r.get('confidence',''):10s} {r['input'][:60]}...{pm}")
| Tier | Runs / hour | Refs / run |
|---|---|---|
| free | 20 | 10 |
| regular | 60 | 30 |