1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
|
# Citation Management & Hallucination Prevention
This reference provides a complete workflow for managing citations programmatically, preventing AI-generated citation hallucinations, and maintaining clean bibliographies.
---
## Contents
- [Why Citation Verification Matters](#why-citation-verification-matters)
- [Citation APIs Overview](#citation-apis-overview)
- [Verified Citation Workflow](#verified-citation-workflow)
- [Python Implementation](#python-implementation)
- [BibTeX Management](#bibtex-management)
- [Common Citation Formats](#common-citation-formats)
- [Troubleshooting](#troubleshooting)
---
## Why Citation Verification Matters
### The Hallucination Problem
Research has documented significant issues with AI-generated citations:
- **~40% error rate** in AI-generated citations (Enago Academy research)
- NeurIPS 2025 found **100+ hallucinated citations** slipped through review
- Common errors include:
- Fabricated paper titles with real author names
- Wrong publication venues or years
- Non-existent papers with plausible metadata
- Incorrect DOIs or arXiv IDs
### Consequences
- Desk rejection at some venues
- Loss of credibility with reviewers
- Potential retraction if published
- Wasted time chasing non-existent sources
### Solution
**Never generate citations from memory—always verify programmatically.**
---
## Citation APIs Overview
### Primary APIs
| API | Coverage | Rate Limits | Best For |
|-----|----------|-------------|----------|
| **Semantic Scholar** | 214M papers | 1 RPS (free key) | ML/AI papers, citation graphs |
| **CrossRef** | 140M+ DOIs | Polite pool with mailto | DOI lookup, BibTeX retrieval |
| **arXiv** | Preprints | 3-second delays | ML preprints, PDF access |
| **OpenAlex** | 240M+ works | 100K/day, 10 RPS | Open alternative to MAG |
### API Selection Guide
```
Need ML paper search? → Semantic Scholar
Have DOI, need BibTeX? → CrossRef content negotiation
Looking for preprint? → arXiv API
Need open data, bulk access? → OpenAlex
```
### No Official Google Scholar API
Google Scholar has no official API. Scraping violates ToS. Use SerpApi ($75-275/month) only if Semantic Scholar coverage is insufficient.
---
## Verified Citation Workflow
### 5-Step Process
```
1. SEARCH → Query Semantic Scholar with specific keywords
↓
2. VERIFY → Confirm paper exists in 2+ sources
↓
3. RETRIEVE → Get BibTeX via DOI content negotiation
↓
4. VALIDATE → Confirm the claim appears in source
↓
5. ADD → Add verified entry to .bib file
```
### Step 1: Search
Use Semantic Scholar for ML/AI papers:
```python
from semanticscholar import SemanticScholar
sch = SemanticScholar()
results = sch.search_paper("transformer attention mechanism", limit=10)
for paper in results:
print(f"Title: {paper.title}")
print(f"Year: {paper.year}")
print(f"DOI: {paper.externalIds.get('DOI', 'N/A')}")
print(f"arXiv: {paper.externalIds.get('ArXiv', 'N/A')}")
print(f"Citation count: {paper.citationCount}")
print("---")
```
### Step 2: Verify Existence
Confirm paper exists in at least two sources:
```python
import requests
def verify_paper(doi=None, arxiv_id=None, title=None):
"""Verify paper exists in multiple sources."""
sources_found = []
# Check Semantic Scholar
sch = SemanticScholar()
if doi:
paper = sch.get_paper(f"DOI:{doi}")
if paper:
sources_found.append("Semantic Scholar")
# Check CrossRef (via DOI)
if doi:
resp = requests.get(f"https://api.crossref.org/works/{doi}")
if resp.status_code == 200:
sources_found.append("CrossRef")
# Check arXiv
if arxiv_id:
resp = requests.get(
f"http://export.arxiv.org/api/query?id_list={arxiv_id}"
)
if "<entry>" in resp.text:
sources_found.append("arXiv")
return len(sources_found) >= 2, sources_found
```
### Step 3: Retrieve BibTeX
Use DOI content negotiation for guaranteed accuracy:
```python
import requests
def doi_to_bibtex(doi: str) -> str:
"""Get verified BibTeX from DOI via CrossRef content negotiation."""
response = requests.get(
f"https://doi.org/{doi}",
headers={"Accept": "application/x-bibtex"},
allow_redirects=True
)
response.raise_for_status()
return response.text
# Example: "Attention Is All You Need"
bibtex = doi_to_bibtex("10.48550/arXiv.1706.03762")
print(bibtex)
```
### Step 4: Validate Claims
Before citing a paper for a specific claim, verify the claim exists:
```python
def get_paper_abstract(doi):
"""Get abstract to verify claims."""
sch = SemanticScholar()
paper = sch.get_paper(f"DOI:{doi}")
return paper.abstract if paper else None
# Verify claim appears in abstract
abstract = get_paper_abstract("10.48550/arXiv.1706.03762")
claim = "attention mechanism"
if claim.lower() in abstract.lower():
print("Claim appears in paper")
```
### Step 5: Add to Bibliography
Add verified entry to your .bib file with consistent key format:
```python
def generate_citation_key(bibtex: str) -> str:
"""Generate consistent citation key: author_year_firstword."""
import re
# Extract author
author_match = re.search(r'author\s*=\s*\{([^}]+)\}', bibtex, re.I)
if author_match:
first_author = author_match.group(1).split(',')[0].split()[-1]
else:
first_author = "unknown"
# Extract year
year_match = re.search(r'year\s*=\s*\{?(\d{4})\}?', bibtex, re.I)
year = year_match.group(1) if year_match else "0000"
# Extract title first word
title_match = re.search(r'title\s*=\s*\{([^}]+)\}', bibtex, re.I)
if title_match:
first_word = title_match.group(1).split()[0].lower()
first_word = re.sub(r'[^a-z]', '', first_word)
else:
first_word = "paper"
return f"{first_author.lower()}_{year}_{first_word}"
```
---
## Python Implementation
### Complete Citation Manager Class
```python
"""
Citation Manager - Verified citation workflow for ML papers.
"""
import requests
import time
from typing import Optional, List, Dict, Tuple
from dataclasses import dataclass
try:
from semanticscholar import SemanticScholar
except ImportError:
print("Install: pip install semanticscholar")
SemanticScholar = None
@dataclass
class Paper:
title: str
authors: List[str]
year: int
doi: Optional[str]
arxiv_id: Optional[str]
venue: Optional[str]
citation_count: int
abstract: Optional[str]
class CitationManager:
"""Manage citations with verification."""
def __init__(self, api_key: Optional[str] = None):
self.sch = SemanticScholar(api_key=api_key) if SemanticScholar else None
self.verified_papers: Dict[str, Paper] = {}
def search(self, query: str, limit: int = 10) -> List[Paper]:
"""Search for papers using Semantic Scholar."""
if not self.sch:
raise RuntimeError("Semantic Scholar not available")
results = self.sch.search_paper(query, limit=limit)
papers = []
for r in results:
paper = Paper(
title=r.title,
authors=[a.name for a in (r.authors or [])],
year=r.year or 0,
doi=r.externalIds.get('DOI') if r.externalIds else None,
arxiv_id=r.externalIds.get('ArXiv') if r.externalIds else None,
venue=r.venue,
citation_count=r.citationCount or 0,
abstract=r.abstract
)
papers.append(paper)
return papers
def verify(self, paper: Paper) -> Tuple[bool, List[str]]:
"""Verify paper exists in multiple sources."""
sources = []
# Already found in Semantic Scholar via search
sources.append("Semantic Scholar")
# Check CrossRef if DOI available
if paper.doi:
try:
resp = requests.get(
f"https://api.crossref.org/works/{paper.doi}",
timeout=10
)
if resp.status_code == 200:
sources.append("CrossRef")
except:
pass
# Check arXiv if ID available
if paper.arxiv_id:
try:
resp = requests.get(
f"http://export.arxiv.org/api/query?id_list={paper.arxiv_id}",
timeout=10
)
if "<entry>" in resp.text and "<title>" in resp.text:
sources.append("arXiv")
except:
pass
return len(sources) >= 2, sources
def get_bibtex(self, paper: Paper) -> Optional[str]:
"""Get BibTeX for verified paper."""
if paper.doi:
try:
resp = requests.get(
f"https://doi.org/{paper.doi}",
headers={"Accept": "application/x-bibtex"},
timeout=10,
allow_redirects=True
)
if resp.status_code == 200:
return resp.text
except:
pass
# Fallback: generate from paper data
return self._generate_bibtex(paper)
def _generate_bibtex(self, paper: Paper) -> str:
"""Generate BibTeX from paper metadata."""
# Generate citation key
first_author = paper.authors[0].split()[-1] if paper.authors else "unknown"
first_word = paper.title.split()[0].lower().replace(',', '').replace(':', '')
key = f"{first_author.lower()}_{paper.year}_{first_word}"
# Format authors
authors = " and ".join(paper.authors) if paper.authors else "Unknown"
bibtex = f"""@article{{{key},
title = {{{paper.title}}},
author = {{{authors}}},
year = {{{paper.year}}},
{'doi = {' + paper.doi + '},' if paper.doi else ''}
{'eprint = {' + paper.arxiv_id + '},' if paper.arxiv_id else ''}
{'journal = {' + paper.venue + '},' if paper.venue else ''}
}}"""
return bibtex
def cite(self, query: str) -> Optional[str]:
"""Full workflow: search, verify, return BibTeX."""
# Search
papers = self.search(query, limit=5)
if not papers:
return None
# Take top result
paper = papers[0]
# Verify
verified, sources = self.verify(paper)
if not verified:
print(f"Warning: Could only verify in {sources}")
# Get BibTeX
bibtex = self.get_bibtex(paper)
# Cache
if bibtex:
self.verified_papers[paper.title] = paper
return bibtex
# Usage example
if __name__ == "__main__":
cm = CitationManager()
# Search and cite
bibtex = cm.cite("attention is all you need transformer")
if bibtex:
print(bibtex)
```
### Quick Functions
```python
def quick_cite(query: str) -> str:
"""One-liner citation."""
cm = CitationManager()
return cm.cite(query)
def batch_cite(queries: List[str], output_file: str = "references.bib"):
"""Cite multiple papers and save to file."""
cm = CitationManager()
bibtex_entries = []
for query in queries:
print(f"Processing: {query}")
bibtex = cm.cite(query)
if bibtex:
bibtex_entries.append(bibtex)
time.sleep(1) # Rate limiting
with open(output_file, 'w') as f:
f.write("\n\n".join(bibtex_entries))
print(f"Saved {len(bibtex_entries)} citations to {output_file}")
```
---
## BibTeX Management
### BibTeX vs BibLaTeX
| Feature | BibTeX | BibLaTeX |
|---------|--------|----------|
| Unicode support | Limited | Full |
| Entry types | Standard | Extended (@online, @dataset) |
| Customization | Limited | Highly flexible |
| Backend | bibtex | Biber (recommended) |
**Recommendation**: Use BibLaTeX with Biber for new papers.
### LaTeX Setup
```latex
% In preamble
\usepackage[
backend=biber,
style=numeric,
sorting=none
]{biblatex}
\addbibresource{references.bib}
% In document
\cite{vaswani_2017_attention}
% At end
\printbibliography
```
### Citation Commands
```latex
\cite{key} % Numeric: [1]
\citep{key} % Parenthetical: (Author, 2020)
\citet{key} % Textual: Author (2020)
\citeauthor{key} % Just author name
\citeyear{key} % Just year
```
### Consistent Citation Keys
Use format: `author_year_firstword`
```
vaswani_2017_attention
devlin_2019_bert
brown_2020_language
```
---
## Common Citation Formats
### Conference Paper
```bibtex
@inproceedings{vaswani_2017_attention,
title = {Attention Is All You Need},
author = {Vaswani, Ashish and Shazeer, Noam and Parmar, Niki and
Uszkoreit, Jakob and Jones, Llion and Gomez, Aidan N and
Kaiser, Lukasz and Polosukhin, Illia},
booktitle = {Advances in Neural Information Processing Systems},
volume = {30},
year = {2017},
publisher = {Curran Associates, Inc.}
}
```
### Journal Article
```bibtex
@article{hochreiter_1997_long,
title = {Long Short-Term Memory},
author = {Hochreiter, Sepp and Schmidhuber, J{\"u}rgen},
journal = {Neural Computation},
volume = {9},
number = {8},
pages = {1735--1780},
year = {1997},
publisher = {MIT Press}
}
```
### arXiv Preprint
```bibtex
@misc{brown_2020_language,
title = {Language Models are Few-Shot Learners},
author = {Brown, Tom and Mann, Benjamin and Ryder, Nick and others},
year = {2020},
eprint = {2005.14165},
archiveprefix = {arXiv},
primaryclass = {cs.CL}
}
```
---
## Troubleshooting
### Common Issues
**Issue: Semantic Scholar returns no results**
- Try more specific keywords
- Check spelling of author names
- Use quotation marks for exact phrases
**Issue: DOI doesn't resolve to BibTeX**
- DOI may be registered but not linked to CrossRef
- Try arXiv ID instead if available
- Generate BibTeX from metadata manually
**Issue: Rate limiting errors**
- Add delays between requests (1-3 seconds)
- Use API key if available
- Cache results to avoid repeat queries
**Issue: Encoding problems in BibTeX**
- Use proper LaTeX escaping: `{\"u}` for ü
- Ensure file is UTF-8 encoded
- Use BibLaTeX with Biber for better Unicode
### Verification Checklist
Before adding a citation:
- [ ] Paper found in at least 2 sources
- [ ] DOI or arXiv ID verified
- [ ] BibTeX retrieved (not generated from memory)
- [ ] Entry type correct (@inproceedings vs @article)
- [ ] Author names complete and correctly formatted
- [ ] Year and venue verified
- [ ] Citation key follows consistent format
---
## Additional Resources
**APIs:**
- Semantic Scholar: https://api.semanticscholar.org/api-docs/
- CrossRef: https://www.crossref.org/documentation/retrieve-metadata/rest-api/
- arXiv: https://info.arxiv.org/help/api/basics.html
- OpenAlex: https://docs.openalex.org/
**Python Libraries:**
- `semanticscholar`: https://pypi.org/project/semanticscholar/
- `arxiv`: https://pypi.org/project/arxiv/
- `habanero` (CrossRef): https://github.com/sckott/habanero
**Verification Tools:**
- Citely: https://citely.ai/citation-checker
- ReciteWorks: https://reciteworks.com/
|