Cito.

How Cito works, where the data comes from, and the API.

DocsNotesAccount
OverviewDataAPILicensing
DocsNotesStatsLicensingPrivacyTerms

A project by FIM

Data from Semantic Scholar and OpenAlex. Corpus snapshot 2026-09-29, synced 3 Oct 2026. Freshness

Cito / documentation

Build with Cito.

Base URL: https://api.cito.fim.ai

llms.txt (for agents)OpenAPI JSONInteractive reference (Swagger) ↗

Authentication

Optional for search. Pass an API key with either header; create keys at Account → API keys. Endpoints under /me always require one.

Authorization: Bearer sk_...
X-API-Key: sk_...

Rate limits

  • Anonymous (no key): 15 requests/min per IP.
  • With an API key: the key's own per-minute limit; keys created without an explicit limit follow the server default (currently 100/min). The enforced limit for each of your keys is shown at Account → API keys.
  • Over the limit: 429 with {"detail": "Rate limit exceeded."}

MCP server

The API doubles as an MCP server (streamable HTTP) at https://api.cito.fim.ai/mcp, exposing search_papers and get_paper as tools. One command in Claude Code:

claude mcp add --transport http cito https://api.cito.fim.ai/mcp

Optionally pass an API key to use the per-key tier instead of the anonymous per-IP limit:

claude mcp add --transport http cito https://api.cito.fim.ai/mcp \
  --header "Authorization: Bearer sk_..."

Any MCP client that speaks streamable HTTP works the same way (Claude Desktop, Cursor, …). MCP calls share the rate tiers and metering of /search.

GET /search

  • q string, requiredNatural-language or keyword query.
  • mode stringhybrid (default) · semantic · keyword
  • limit intMax results. Default 20, max 100.
  • enrich boolAttach title/abstract/authors from the local metadata store. Default true.
  • published_before string, YYYY-MM-DDLeak-safe date gate: only papers published strictly before this date, filtered before the pool is cut to limit. Year-only papers are kept iff the year is strictly earlier; same-year and undated papers are excluded.
  • include_imprecise boolWith published_before: also keep papers whose date is too coarse to prove precedence (same-year year-granularity, undated). Default false.
curl "https://api.cito.fim.ai/search?q=contrastive+learning&limit=5"

Response:

{
  "query": "attention is all you need",
  "mode": "hybrid",
  "took_ms": 257,
  "hits": [
    {
      "id": "13756489",
      "title": "Attention is All you Need",
      "abstract": "...",
      "authors": ["Ashish Vaswani", "..."],
      "year": 2017,
      "publication_date": "2017-06-12",
      "date_granularity": "day",
      "venue": "NeurIPS",
      "doi": "...",
      "url": "https://www.semanticscholar.org/p/13756489",
      "pdf_url": "...",
      "cites": 100000,
      "score": 0.97
    }
  ],
  "corpus_release": "2026-06-24",
  "corpus_synced_at": "2026-07-15T04:12:07Z",
  "attribution": "Data from Semantic Scholar"
}

id is a Semantic Scholar CorpusID. doi, pdf_url, cites and publication_date may be null; date_granularity is day, year or null. Ordering is deterministic: the same query on the same snapshot replays identically (ties broken by CorpusID). Errors: 401 (invalid key), 422 (malformed published_before), 429 (rate limit), 503 (backend temporarily down).

GET /paper/{corpusid}

One paper's metadata by CorpusID: the same fields as a search hit minus score. 404 if unknown.

curl "https://api.cito.fim.ai/paper/13756489"

GET /status

Index and feature status: auth_required, rerank_enabled, index counts, and corpus freshness — corpus_release (the Semantic Scholar release being served) plus corpus_synced_at (when it finished applying, ISO-8601 UTC). Search responses carry the same pair, so a client judging a time-sensitive query needs no second call. Cheap and Postgres-free: safe to use as a readiness probe when /stats is unavailable.

GET /me · GET /me/usage

Your account, keys and recent usage. Both require an API key.

Python

import requests

r = requests.get(
    "https://api.cito.fim.ai/search",
    params={"q": "attention is all you need", "limit": 5},
    headers={"Authorization": "Bearer sk_..."},  # optional
    timeout=30,
)
for hit in r.json()["hits"]:
    print(hit["id"], hit["title"])

Data & attribution

Data from Semantic Scholar and OpenAlex; see Licensing. Responses carry an attribution field; keep it when you republish results.