Verifica citazioni via programma. Un endpoint, in streaming via SSE.
Base URL
https://api.citetrue.comAvvio rapido
- Vai su Dashboard → Chiavi API e clicca Nuova chiave API. Copia la chiave che inizia con
sk_. È mostrata una sola volta. - Includi la chiave in
Authorization: Bearer sk_…su ogni richiesta. - POST un task a
/verify/v2. La risposta ètext/event-stream; consuma eventi SSE fino a vederetask_completed.
Chiamata minima deep-verify:
curl -N -X POST https://api.citetrue.com/verify/v2 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"depth":5,"text":"[1] Vaswani, A., Shazeer, N., Parmar (2017). Attention is all you need."}'-N disabilita il buffering di curl così gli eventi SSE arrivano riga per riga. "depth": 5 fa passare ogni job per la pipeline deep completa (DB più ampi + AI più potente); "depth": 1 per un controllo rapido più economico.
Esempi client
SSE è una risposta HTTP a lunga durata dove ogni evento termina con una riga vuota (\n\n). Salta le righe che non iniziano con data: e JSON-parse il resto.
Nota browser: EventSource integrato non può inviare header Authorization — usa fetch + ReadableStream come sotto.
Node 18+ with native fetch.
const API = 'https://api.citetrue.com'
const KEY = process.env.API_KEY
async function verify(text) {
const resp = await fetch(`${API}/verify/v2`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ depth: 5, text }),
})
if (!resp.ok) throw new Error(`${resp.status}: ${await resp.text()}`)
const reader = resp.body.getReader()
const decoder = new TextDecoder()
const refs = []
let buf = ''
while (true) {
const { value, done } = await reader.read()
if (done) break
buf += decoder.decode(value, { stream: true })
let idx
while ((idx = buf.indexOf('\n\n')) !== -1) {
const chunk = buf.slice(0, idx); buf = buf.slice(idx + 2)
for (const line of chunk.split('\n')) {
if (!line.startsWith('data: ')) continue
const evt = JSON.parse(line.slice(6))
if (evt.event === 'ref_completed') {
refs.push(evt.data.ref)
}
if (evt.event === 'task_completed') return { refs, balance: evt.data.balance }
if (evt.event === 'error') throw new Error(evt.error)
}
}
}
}
const out = await verify('[1] Vaswani, A., Shazeer, N., Parmar (2017). Attention is all you need.')
console.log(out)Autenticazione
Le chiavi API sono token bearer di lunga durata della forma sk_<40-char-token>. Inviale nell'header Authorization:
Authorization: Bearer sk_AbCd123…- Revoca una chiave dalla Dashboard. Il revoco ha effetto in pochi secondi.
- Una chiave può fare tutto ciò che il suo utente può fare. Trattala come una password — ruota e limita per integrazione.
Limiti di velocità
Ogni API key ha un limitatore token-bucket:
- Burst: 10 richieste.
- Sostenuto: 60 richieste/minuto (bucket ricaricato a 1 token/s).
- Al superamento, la richiesta fallisce con
429e un headerRetry-After(secondi).
Il limitatore opera per-key; chiavi diverse dello stesso account hanno bucket indipendenti. I crediti si applicano in aggiunta.
Header richiesta
Every endpoint accepts the same authentication and content-type headers; only the request body and URL differ.
| Header | Obbligatorio | Valore |
|---|---|---|
| Authorization | yes | Bearer sk_… |
| Content-Type | yes | application/json |
Responses are 200 OK with Content-Type: text/event-stream.
data: {"event":"<name>","data":{...},"error":""}POST /verify/v2
Split a text blob into references and verify each. Same endpoint covers three modes — supply exactly one of text, refHash, or taskHash.
Corpo richiesta
| Campo | Tipo | Note |
|---|---|---|
| text | string | New verify task. Max 10MB. Accepts numbered, bulleted, blank-line-separated, BibTeX, or in-text prose with (Author, Year) citations (depth ≥ 5 only). |
| refHash | string | Upgrade a single prior reference to a deeper run. Requires depth ≥ 5 and parentTaskHash. |
| parentTaskHash | string | The task this reference originally belonged to. Required when using refHash. |
| taskHash | string | Resume an existing task — replays cached state or attaches to live stream. No re-billing for already-charged refs. |
| depth | int / string | 1 (default) / 5 / 20. Selects verification level + cost. |
| force | bool | Bypass the task-level dedup cache (forces a fresh run, charges full cost). |
| locale | string | Optional 2-letter language code ("zh", "ja", "de", …). When set, AI translates the per-ref note into that language and surfaces it under noteTranslated[locale]. Default "" / "en" = no translation. |
Parametri — depth
1— fast: handles structured reference lists and inline prose (auto-falls back to AI splitter on hard inputs). 1 credit per reference.5— deep: AI-driven verification; handles unusual formats and gives a stronger verdict. Each reference first gets a fast pass; if that already finds it authentic, it's billed just 1 credit and deep verification is skipped — otherwise 5 (see Credits & billing below).20— closed beta, not enabled for public API. Same flow as5with a higher-tier model.
Idempotenza: same text + same depth + same user returns the cached task on second POST (no re-billing) unless force: true.
Richiesta esempio (text, depth=1)
{
"text": "[1] Vaswani, A., Shazeer, N., Parmar (2017). Attention is all you need.\n[2] Piketty, T. (2016). Capital in the twenty-first century. Harvard University Press."
}Richiesta esempio (da testo) (depth=5)
{
"depth": 5,
"text": "[1] Vaswani, A., Shazeer, N., Parmar (2017). Attention is all you need."
}Richiesta esempio (job singolo) (refHash upgrade)
{
"depth": 5,
"refHash": "a1b2c3d4e5f6...",
"parentTaskHash": "f0e1d2c3b4a5..."
}Richiesta esempio (resume)
{
"taskHash": "f0e1d2c3b4a5..."
}Sequenza tipica di eventi
task_created → refs_created (N refs) →
[ref_processing + ref_completed] × N (parallel) +
task_progress × several →
task_completedPer-ref assessment
Each ref_completed event carries an assessment string from this closed set:
authentic— paper exists and is the one cited.valuecontains the matched paper.unsure— found something but can't confirm;noticesandconfidencedescribe why.inauthentic— definitively not found / fabricated / unrelated.invalid— input wasn't a citation; pipeline refused to search before any datasource hit.error— verification failed on this single ref; sibling refs may still succeed.data.errors[]lists tokens (datasource_unavailable,timeout,rate_limited,parse_failed).exceeded— account ran out of credits before this ref could run.
Notices on a result (year mismatch, author mismatch, …) are orthogonal to assessment — even an authentic verdict can carry notices worth showing. data.depthreports the highest depth that has run on this ref (1 / 5 / 20); clients derive a "deep-verified" flag locally as depth ≥ 5.
Eventi SSE
SSE event names emitted during verification. Listen for task_completed as the terminal success event; error for terminal failure.
task_created— task accepted, hash assigned.refs_created— text was split into N refs.split_completed— splitting phase finished.ref_processing/ref_status— per-ref progress signal.ref_completed— per-ref final assessment.task_completed— all refs done; carriesbalance.task_progress— periodic progress %.warn— non-fatal warning.error— terminal task-level failure.
GET /credits
GET /credits/v1 — returns current credit balance. Auth required.
Crediti e fatturazione
1 credit per ref for depth=1, 5 credits per ref for depth=5. Cached tasks (same text + depth + user) do not re-bill.
Deep verification billing: at depth=5 each reference first gets a fast verification pass. If that already finds it authentic, it's billed just 1 credit and deep verification is skipped; otherwise it's billed 5. The per-ref cost and task_completed totals are authoritative. A single-ref refHash upgrade always runs the full deep verification (5).
Riferimento errori
Standard HTTP status codes apply (400 invalid, 401 auth missing/invalid, 402 insufficient credits, 429 rate-limited, 5xx upstream). SSE-level failures arrive as a final error event.
Versioning e stabilità
Path-versioned (/verify/v2 etc.). Breaking changes bump the version segment; backwards-compatible additions ship in-place.