API documentation

Four endpoints, one auth header, billing per resolved citation. Everything you need to integrate — every example here was run against production.

Authentication

Bearer token in the header. An unknown key, a disabled key and a missing header all return the same 401 — we do not hint which one failed.

Authorization: Bearer cyt_live_…

Two kinds of keys

A cyt_test_ key uses the same corpus and returns the same answers but is never billed — 300 citations a month for integration and error handling. A cyt_live_ key serves production traffic and counts towards your plan. The key is shown exactly once when issued; we store only its hash.

What you pay for

The billing unit is a RESOLVED CITATION — one sentence matched to a source with a page number. A miss (found: false) is free. Formatting never touches the corpus and is free on every plan. A NEW TOPIC is metered separately: building the corpus for a subject we do not have yet takes minutes and costs two orders of magnitude more, which is why you trigger it deliberately with harvestOnMiss.

PlanPrice / moCitationsNew topicsOverage
Testfree1003
Start$491,50020$0.04 / $1.00
Pro$1997,000100$0.04 / $1.00
Scale$79930,000500$0.04 / $1.00

Endpoints

GET /usage

Plan state and usage for the current month. Costs nothing and consumes nothing — the cheapest way to check that a key works.

curl -s -H "Authorization: Bearer $CYTADO_KEY" \
  https://cytado.com/api/ext/usage
import os, requests

r = requests.get(
    "https://cytado.com/api/ext/usage",
    headers={"Authorization": f"Bearer {os.environ['CYTADO_KEY']}"},
    timeout=60,
)
print(r.json())
const r = await fetch("https://cytado.com/api/ext/usage", {
  headers: { Authorization: `Bearer ${process.env.CYTADO_KEY}` },
});
console.log(await r.json());

POST /find-source

A sentence in, a source with a page number out. The only billable call — and only when it finds something. Up to 60 items per request; the excess is dropped EXPLICITLY. The hint field is the topic of the work: without it we ask “does this source support this sentence”, with it “could someone writing THIS paper cite it”.

curl -s -X POST https://cytado.com/api/ext/find-source \
  -H "Authorization: Bearer $CYTADO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "id": "a1",
      "claim": "Prokrastynacji towarzyszy poczucie winy i dyskomfort psychiczny.",
      "hint": "prokrastynacja u studentow"
    }
  ],
  "langs": [
    "pl"
  ],
  "harvestOnMiss": false
}'
import os, requests

r = requests.post(
    "https://cytado.com/api/ext/find-source",
    headers={"Authorization": f"Bearer {os.environ['CYTADO_KEY']}"},
    json={
  "items": [
    {
      "id": "a1",
      "claim": "Prokrastynacji towarzyszy poczucie winy i dyskomfort psychiczny.",
      "hint": "prokrastynacja u studentow"
    }
  ],
  "langs": [
    "pl"
  ],
  "harvestOnMiss": False
},
    timeout=600,
)
r.raise_for_status()
print(r.json())
const r = await fetch("https://cytado.com/api/ext/find-source", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CYTADO_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "items": [
    {
      "id": "a1",
      "claim": "Prokrastynacji towarzyszy poczucie winy i dyskomfort psychiczny.",
      "hint": "prokrastynacja u studentow"
    }
  ],
  "langs": [
    "pl"
  ],
  "harvestOnMiss": false
}),
});
if (!r.ok) throw new Error(`cytado ${r.status}: ${await r.text()}`);
console.log(await r.json());

POST /format

Metadata in, a formatted footnote and bibliography entry out. Styles: apa7 (alias apa), mla, chicago, pl-footnote. Use include to pick the forms: footnote, entry, bibtex, ris. Never touches the corpus, so it is free on every plan.

curl -s -X POST https://cytado.com/api/ext/format \
  -H "Authorization: Bearer $CYTADO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "style": "apa",
  "include": [
    "footnote",
    "entry",
    "bibtex",
    "ris"
  ],
  "sources": [
    {
      "title": "Regulacja nastroju a prokrastynacja",
      "authors": "Pisarska, A.",
      "year": 2020,
      "page": 208
    }
  ]
}'
import os, requests

r = requests.post(
    "https://cytado.com/api/ext/format",
    headers={"Authorization": f"Bearer {os.environ['CYTADO_KEY']}"},
    json={
  "style": "apa",
  "include": [
    "footnote",
    "entry",
    "bibtex",
    "ris"
  ],
  "sources": [
    {
      "title": "Regulacja nastroju a prokrastynacja",
      "authors": "Pisarska, A.",
      "year": 2020,
      "page": 208
    }
  ]
},
    timeout=600,
)
r.raise_for_status()
print(r.json())
const r = await fetch("https://cytado.com/api/ext/format", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CYTADO_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "style": "apa",
  "include": [
    "footnote",
    "entry",
    "bibtex",
    "ris"
  ],
  "sources": [
    {
      "title": "Regulacja nastroju a prokrastynacja",
      "authors": "Pisarska, A.",
      "year": 2020,
      "page": 208
    }
  ]
}),
});
if (!r.ok) throw new Error(`cytado ${r.status}: ${await r.text()}`);
console.log(await r.json());

POST /chapter-sources

A chapter topic in, a set of citable sources out, ranked by topic coverage. The exclude field lists sources already used — coverage is then computed over what remains, so consecutive chapters do not keep receiving the same material.

curl -s -X POST https://cytado.com/api/ext/chapter-sources \
  -H "Authorization: Bearer $CYTADO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "topic": "czas ekranowy dzieci w wieku przedszkolnym",
  "chapter": "Wprowadzenie",
  "exclude": []
}'
import os, requests

r = requests.post(
    "https://cytado.com/api/ext/chapter-sources",
    headers={"Authorization": f"Bearer {os.environ['CYTADO_KEY']}"},
    json={
  "topic": "czas ekranowy dzieci w wieku przedszkolnym",
  "chapter": "Wprowadzenie",
  "exclude": []
},
    timeout=600,
)
r.raise_for_status()
print(r.json())
const r = await fetch("https://cytado.com/api/ext/chapter-sources", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CYTADO_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "topic": "czas ekranowy dzieci w wieku przedszkolnym",
  "chapter": "Wprowadzenie",
  "exclude": []
}),
});
if (!r.ok) throw new Error(`cytado ${r.status}: ${await r.text()}`);
console.log(await r.json());

Response fields worth understanding

found
false means the corpus has nothing to support this claim. We do not return the nearest match instead, and we do not charge.
page_from / page_to
The PRINTED page number — the one a supervisor checking your footnote will land on, not the sheet number inside the PDF.
page_from_label
Filled only for unusual numbering (e.g. “E136”). An empty label with a filled page_from means “the printed number is simply 152”, not “no pagination”.
relevance
9–10: directly about this claim and this group. 7–8: the same phenomenon in a related setting — different profession, age or country; citable, but mark the context.
url
A link to the original — a DOI or a direct address. EVERY returned source has one; for more than half the corpus it opens the full text, because those are open-access publications.
snippet
A short excerpt under fair use. We never return the full text of sources we may not republish — we return the link under which the reader can read them at the publisher.
rozliczenie
Exactly what this request was charged for: number of citations and number of new topics.

Limits and errors

401 unauthorized
Unknown, disabled or missing key.
400 invalid-json
The request body is not valid JSON.
400 missing-items
The required `items` array is missing.
422 invalid_request
Syntactically valid but not acceptable — e.g. an unknown citation style.
429 rate_limited
Limit exceeded. Retry-After says how many seconds to wait. The per-minute guard means “slow down”, the daily limit means “done for today” — the message distinguishes them.
402
Plan exhausted on an operation that requires paid access.

Start with a test key

300 citations a month, never billed, no card. You issue the key yourself in the panel, right after signing up.

Create an account