Developer

API Documentation

Programmatic access to 600,000+ CS/AI/ML research papers with LLM-generated novelty scoring, semantic similarity, and citation graph data. Available via REST API or the Scholar Feed MCP server for AI coding assistants.

Quick Start

  1. 1. Get an API key

    Generate a key at the API keys page. Keys start with sf_.

  2. 2. Make your first request

    curl -H "Authorization: Bearer sf_your_key_here" \
      "https://api.scholarfeed.org/v1/papers/search?q=attention+mechanism&limit=5"
  3. 3. Or install the MCP server for Claude Code / Cursor

    npx scholar-feed-mcp init

    The init wizard detects your MCP client and writes the config automatically.

Authentication

All requests require an Authorization: Bearer sf_your_key header. Keys are in the format sf_ followed by 40 hex characters. Manage your keys at /developers/keys.

curl -H "Authorization: Bearer sf_your_key_here" \
  "https://api.scholarfeed.org/v1/health"

Base URL

https://api.scholarfeed.org/v1

Legacy paths at /api/v1/public/* remain available for existing integrations.

OpenAPI Spec

A machine-readable OpenAPI 3.x spec is published at /v1/openapi.json — import it into Postman, openapi-generator, or your client SDK.

https://api.scholarfeed.org/v1/openapi.json

Rate Limits

Limits are per account (shared across all your API keys). The daily volume quota is 100 calls/day anonymous, 1,000/day on a free account, and 10,000/day on Pro. The per-minute limits below are per account too. POST /v1/embeddings is a Pro-only endpoint (anonymous and free callers get a 403 pro_required). Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. When you receive a 429, the Retry-After header tells you how many seconds to wait before retrying.

MethodEndpointPer-min
GET/v1/health60/min
GET/v1/papers/search30/min
GET/v1/papers30/min
GET/v1/papers/{arxiv_id}60/min
GET/v1/papers/{arxiv_id}/similar20/min
GET/v1/papers/{arxiv_id}/citations30/min
GET/v1/papers/{arxiv_id}/citations/about20/min
GET/v1/papers/{arxiv_id}/fulltext10/min
GET/v1/papers/{arxiv_id}/repo10/min
POST/v1/papers/bibtex20/min
GET/v1/trending30/min
GET/v1/authors/discover20/min
GET/v1/authors/co-author-graph20/min
GET/v1/authors/{author_id}30/min
GET/v1/authors/{author_id}/papers20/min
GET/v1/research/landscape10/min
POST/v1/embeddings30/min
GET/v1/field-orientation20/min
POST/v1/batches10/min
GET/v1/batches/{batch_id}60/min

Endpoints

GET/v1/health
60/min

Connection check. Verifies your API key is valid and returns your plan, key name, and today's usage count.

{
  "status": "ok",
  "plan": "free",
  "key_name": "my-research-key",
  "usage_today": 47
}

Papers

GET/v1/papers/search
30/min

Full-text and semantic search across 600,000+ papers. Results are sorted by rank_score. Supports keyset pagination via cursor for stable result sets across pages. Also supports anchor_paper_id= for similar-paper lookup and scope_to_citations_of= for citation-scoped search.

ParameterTypeRequiredDescription
qstringrequiredSearch query (supports phrase and boolean operators)
categorystringoptionalFilter by arXiv category, e.g. "cs.AI", "cs.LG", "stat.ML"
novelty_minfloatoptionalMinimum llm_novelty_score (0.0–1.0). Use 0.5 to filter for genuinely novel work.
daysintoptionalRestrict to papers published within the last N days
sortstringoptional"rank" (default) or "recent" or "trending"
anchor_paper_idstringoptionalarXiv ID of a seed paper; results are semantically similar papers
scope_to_citations_ofstringoptionalRestrict search to papers that cite the given arXiv ID
limitintoptionalResults per page (default 20, max 50)
cursorstringoptionalKeyset pagination cursor from next_cursor in a previous response
fieldsstringoptionalComma-separated list of fields to return. Default: lean 12-field set.
{
  "papers": [
    {
      "arxiv_id": "2401.04088",
      "title": "Attention Is All You Need (But Not All You Get)",
      "authors": ["A. Researcher", "B. Scientist"],
      "year": 2024,
      "categories": ["cs.LG", "cs.AI"],
      "primary_category": "cs.LG",
      "arxiv_url": "https://arxiv.org/abs/2401.04088",
      "has_code": true,
      "github_url": "https://github.com/example/repo",
      "citation_count": 42,
      "rank_score": 0.73,
      "llm_summary": "Proposes a sparse attention variant that reduces compute by 60% while matching dense attention accuracy on 5 benchmarks.",
      "llm_novelty_score": 0.55
    }
  ],
  "total": 1847,
  "page": 1,
  "limit": 20,
  "next_cursor": "eyJzIjogMC43MywgImlkIjogIjI0MDEuMDQwODgifQ=="
}
GET/v1/papers
30/min

Batch lookup multiple papers by arXiv ID in a single request. Uses repeated query parameters (?arxiv_ids[]=A&arxiv_ids[]=B). IDs not found in the corpus are listed in not_found. Replaces the former POST /papers/batch endpoint.

ParameterTypeRequiredDescription
arxiv_ids[]stringrequiredRepeated arXiv IDs to batch-fetch, e.g. ?arxiv_ids[]=2401.1&arxiv_ids[]=2402.2 (max 50). Version suffixes stripped.
fieldsstringoptionalComma-separated fields to return. Default: lean 12-field set.
verbosebooloptionalIf true, returns the full paper shape. Default false.
{
  "papers": [
    { "arxiv_id": "2401.04088", "title": "Attention Is All You Need (But Not All You Get)", "rank_score": 0.73 },
    { "arxiv_id": "2312.08901", "title": "Efficient Sparse Transformers for Long Sequences", "rank_score": 0.68 }
  ],
  "total": 2,
  "not_found": ["9999.99999"]
}
GET/v1/papers/{arxiv_id}
60/min

Retrieve a single paper by arXiv ID. Version suffixes are stripped automatically (e.g. 2401.12345v2 → 2401.12345).

ParameterTypeRequiredDescription
arxiv_idstringrequiredarXiv paper ID, e.g. "2401.04088" or "2401.04088v2"
fieldsstringoptionalComma-separated fields to return. Pass "abstract" to include the full abstract.
{
  "arxiv_id": "2401.04088",
  "title": "Attention Is All You Need (But Not All You Get)",
  "authors": ["A. Researcher", "B. Scientist"],
  "year": 2024,
  "categories": ["cs.LG", "cs.AI"],
  "primary_category": "cs.LG",
  "arxiv_url": "https://arxiv.org/abs/2401.04088",
  "pdf_url": "https://arxiv.org/pdf/2401.04088",
  "has_code": true,
  "github_url": "https://github.com/example/repo",
  "citation_count": 42,
  "rank_score": 0.73,
  "llm_summary": "Proposes a sparse attention variant that reduces compute by 60% while matching dense attention accuracy on 5 benchmarks.",
  "llm_significance": "Significant for large-scale transformer training; reduces memory cost without accuracy loss.",
  "llm_novelty_score": 0.55,
  "venue_name": "NeurIPS"
}
GET/v1/papers/{arxiv_id}/similar
20/min

Find papers similar to the given paper using precomputed neighbors (65% embedding cosine similarity + 25% bibliographic coupling + 10% direct-citation bonus).

ParameterTypeRequiredDescription
arxiv_idstringrequiredarXiv paper ID of the source paper
limitintoptionalNumber of similar papers to return (default 10, max 30)
daysintoptionalRestrict similar papers to those published within N days
fieldsstringoptionalComma-separated fields to return
{
  "papers": [
    {
      "arxiv_id": "2312.08901",
      "title": "Efficient Sparse Transformers for Long Sequences",
      "similarity_score": 0.87
    }
  ],
  "total": 10
}
GET/v1/papers/{arxiv_id}/citations
30/min

Get the citation graph for a paper. direction='cited_by' returns papers that cite this paper (incoming); direction='citing' returns papers this paper cites (outgoing).

ParameterTypeRequiredDescription
arxiv_idstringrequiredarXiv paper ID
directionstringoptional"cited_by" (default) or "citing"
limitintoptionalNumber of papers to return (default 20, max 50)
fieldsstringoptionalComma-separated fields to return
{
  "papers": [
    {
      "arxiv_id": "2403.11234",
      "title": "Scaling Laws for Sparse Attention"
    }
  ],
  "total": 15,
  "direction": "cited_by"
}
GET/v1/papers/{arxiv_id}/citations/about
20/min

Find papers in the citation graph that match a topic query (e.g. 'citations of AIAYN about protein folding'). Takes the top 2000 citation neighbours by rank_score, then re-ranks them by cosine similarity to a Gemini query embedding.

ParameterTypeRequiredDescription
arxiv_idstringrequiredarXiv paper ID
querystringrequiredNatural-language topic to filter the citation graph by (2–300 chars)
directionstringoptional"cited_by" (default) or "citing"
limitintoptionalNumber of papers to return (default 20, max 50)
fieldsstringoptionalComma-separated fields to return
{
  "papers": [
    {
      "arxiv_id": "2406.11409",
      "title": "CodeGemma: Open Code Models Based on Gemma",
      "llm_summary": "...",
      "similarity_score": 0.702
    },
    {
      "arxiv_id": "2406.11925",
      "title": "DocCGen: Document-based Controlled Code Generation",
      "similarity_score": 0.7009
    }
  ],
  "total": 2,
  "direction": "cited_by",
  "query": "code generation"
}
GET/v1/papers/{arxiv_id}/fulltext
10/min

Extract results and experiments sections from the paper's LaTeX source. Uses a cache-aside pattern — cached papers respond instantly; uncached papers are fetched JIT from arXiv and may take 2–5 seconds.

Rate limit is conservative because JIT extraction downloads arXiv LaTeX source. Please do not hammer this endpoint in bulk — use GET /v1/papers for metadata.

ParameterTypeRequiredDescription
arxiv_idstringrequiredarXiv paper ID
{
  "arxiv_id": "2401.04088",
  "results_text": "Our method achieves 94.2% accuracy on ImageNet, surpassing the dense baseline by 1.8% while using 60% fewer FLOPs per token...",
  "table_captions": [
    "Table 1: Accuracy vs. compute comparison on ImageNet",
    "Table 2: Ablation study on sparsity patterns"
  ],
  "source": "cache"
}
GET/v1/papers/{arxiv_id}/repo
10/min

Fetch the GitHub repository README and file tree for a paper that has an associated code repository. Returns 404 if the paper has no code_url.

ParameterTypeRequiredDescription
arxiv_idstringrequiredarXiv paper ID
{
  "owner": "example",
  "repo": "sparse-attention",
  "readme": "# Sparse Attention\n\nEfficient sparse attention implementation...",
  "file_tree": [
    "README.md",
    "train.py",
    "models/sparse_attn.py",
    "configs/imagenet.yaml"
  ],
  "tree_truncated": false
}
POST/v1/papers/bibtex
20/min

Export BibTeX entries for up to 50 arXiv papers. Returns a single BibTeX string with all found entries concatenated.

ParameterTypeRequiredDescription
arxiv_idsstring[]requiredArray of arXiv IDs (max 50). Version suffixes are stripped automatically.
{
  "bibtex": "@article{2401_04088,\n  title={Attention Is All You Need (But Not All You Get)},\n  author={A. Researcher and B. Scientist},\n  year={2024},\n  journal={arXiv preprint arXiv:2401.04088},\n  url={https://arxiv.org/abs/2401.04088}\n}",
  "count": 1,
  "not_found": []
}

Trending

GET/v1/trending
30/min

Get trending papers from the last 7 days, ranked by a 50/50 blend of rank_score and citation velocity. Uses a precomputed daily cache for fast response.

ParameterTypeRequiredDescription
categorystringoptionalFilter by arXiv category, e.g. "cs.AI"
limitintoptionalNumber of papers to return (default 20, max 50)
fieldsstringoptionalComma-separated fields to return
{
  "papers": [
    {
      "arxiv_id": "2503.12345",
      "title": "State Space Models Strike Back",
      "rank_score": 0.91
    }
  ],
  "total": 20
}

Authors

GET/v1/authors/discover
20/min

Discover researchers by topic (semantic search) or name. Short queries use name-based ILIKE search; longer or research-themed queries use semantic embedding search against author research topic embeddings.

ParameterTypeRequiredDescription
qstringrequiredTopic or researcher name to search (2–500 chars)
fieldstringoptionalFilter by primary field, e.g. "cs.LG"
limitintoptionalNumber of results (default 20, max 50)
{
  "query": "diffusion models",
  "search_type": "semantic",
  "authors": [
    {
      "id": 12345,
      "name": "Jascha Sohl-Dickstein",
      "h_index": 28,
      "total_papers": 42,
      "primary_field": "cs.LG",
      "recent_paper_count": 7,
      "author_rank_score": 0.89,
      "research_topics": ["diffusion", "generative models", "score matching"],
      "semantic_scholar_id": "1234567",
      "years_active": 12,
      "relevance_score": 0.91
    }
  ],
  "total": 1
}
GET/v1/authors/co-author-graph
20/min

Co-authorship edge graph for a set of authors. Returns outgoing edges to every co-author on papers within the time window. Capped at 500 edges per request. Useful for reviewer-triage and collaboration-neighborhood workflows.

ParameterTypeRequiredDescription
author_idsstringrequiredComma-separated author IDs (max 25), e.g. "12345,67890"
window_yearsintoptionalOnly count co-authorships from the last N years (1–30, default 10)
{
  "queried_author_ids": [12345, 67890],
  "window_years": 10,
  "edges": [
    {
      "from": 12345,
      "to": 67890,
      "papers_count": 5,
      "last_collab_year": 2024
    },
    {
      "from": 12345,
      "to": 11111,
      "papers_count": 2,
      "last_collab_year": 2023
    }
  ],
  "edge_count": 2
}
GET/v1/authors/{author_id}
30/min

Get author details by ID. Returns author profile with h-index, rank, top papers, and research topics. Use discover to find author IDs first.

ParameterTypeRequiredDescription
author_idintrequiredNumeric author ID (from discover or co-author-graph)
{
  "id": 12345,
  "name": "Jascha Sohl-Dickstein",
  "h_index": 28,
  "total_papers": 42,
  "total_citations": 18400,
  "avg_novelty_score": 0.71,
  "peak_novelty_score": 0.95,
  "author_rank_score": 0.89,
  "author_alltime_score": 0.84,
  "primary_field": "cs.LG",
  "recent_paper_count": 7,
  "venue_score": 0.82,
  "code_score": 0.65,
  "years_active": 12,
  "semantic_scholar_id": "1234567",
  "research_topics": ["diffusion", "generative models", "score matching"],
  "rank": 142,
  "top_papers": [
    {
      "arxiv_id": "2006.11239",
      "title": "Denoising Diffusion Probabilistic Models",
      "llm_novelty_score": 0.95,
      "year": 2020,
      "citation_count": 9800,
      "rank_score": 0.97
    }
  ]
}
GET/v1/authors/{author_id}/papers
20/min

Get all papers by an author, sorted by rank_score. Returns paginated PublicPaperResponse list.

ParameterTypeRequiredDescription
author_idintrequiredNumeric author ID
limitintoptionalResults per page (default 20, max 50)
pageintoptionalPage number (default 1)
{
  "author_id": 12345,
  "author_name": "Jascha Sohl-Dickstein",
  "papers": [
    {
      "arxiv_id": "2006.11239",
      "title": "Denoising Diffusion Probabilistic Models",
      "year": 2020,
      "citation_count": 9800,
      "rank_score": 0.97
    }
  ],
  "total": 42,
  "page": 1,
  "limit": 20,
  "has_more": true
}

Research

GET/v1/research/landscape
10/min

Aggregated research landscape statistics for a topic. Uses semantic search to find relevant papers, then returns count-based aggregates: authors, publication velocity by month, and novelty score distribution.

ParameterTypeRequiredDescription
qstringrequiredResearch topic (semantic search, 2–500 chars)
limitintoptionalNumber of papers to analyze (10–200, default 50)
{
  "query": "diffusion models for image generation",
  "paper_count": 50,
  "avg_similarity": 0.7823,
  "authors": [
    { "name": "Jascha Sohl-Dickstein", "paper_count": 4 }
  ],
  "publication_velocity": [
    { "month": "2022-01", "count": 3 },
    { "month": "2022-02", "count": 5 }
  ],
  "novelty_distribution": {
    "0.0-0.2": 2, "0.2-0.4": 8, "0.4-0.6": 18, "0.6-0.8": 16, "0.8-1.0": 6
  },
  "avg_novelty": 0.5842
}

Embeddings

POST/v1/embeddings
30/min

Embed text using Gemini Flash embeddings (768-dim). Returns an embedding vector. task_type=RETRIEVAL_DOCUMENT (default) matches paper-side embeddings for HyDE flows; use RETRIEVAL_QUERY for query-side semantic search. Returns 502 if the upstream Gemini service fails.

ParameterTypeRequiredDescription
textstringrequiredText to embed (1–8000 chars)
task_typestringoptional"RETRIEVAL_DOCUMENT" (default) or "RETRIEVAL_QUERY"
{
  "embedding": [0.0124, -0.0381, 0.0562, "... 768 dims total ..."],
  "model": "gemini-embedding-001",
  "dimensionality": 768,
  "task_type": "RETRIEVAL_DOCUMENT"
}
GET/v1/field-orientation
20/min

Cheap retrieval-only field orientation. Embeds the topic via Gemini Flash, then re-ranks candidates by a foundational-ness blend (60% normalised citation count + 40% cosine similarity). No LLM synthesis — the /field-guide skill composes this with in-context reasoning client-side.

ParameterTypeRequiredDescription
topicstringrequiredResearch area to orient on (5–300 chars), e.g. "diffusion models for protein structure"
limitintoptionalNumber of foundational candidate papers to return (5–30, default 15)
{
  "topic": "diffusion models for protein structure",
  "papers": [
    {
      "arxiv_id": "2210.01776",
      "title": "Diffusion probabilistic modeling of protein backbone structure",
      "authors": ["B. Trippe", "J. Yim"],
      "published_date": "2022-10-04",
      "citation_count": 287,
      "rank_score": 0.84,
      "llm_summary": "Adapts DDPM to generate protein backbone coordinates...",
      "llm_novelty_score": 0.78,
      "similarity": 0.8312
    }
  ],
  "total": 15,
  "note": "Cheap retrieval only — no LLM synthesis. The /field-guide skill composes this with in-context reasoning."
}

Batch Lane

POST/v1/batches
10/min

Submit an async batch of operations. Requires an Idempotency-Key header — retrying with the same key (within the account) returns the existing job id, never a duplicate row. Returns 202 with the job id and an initial status of 'queued'. Poll GET /v1/batches/{batch_id} for progress and results.

Idempotency-Key is sent as a request header, not a body field. Batch submission is accepted and persisted, but the processing worker is discovery-gated (BATCH2-01) and not yet active — submitted jobs stay 'queued' until it ships.

ParameterTypeRequiredDescription
requestsobject[]requiredOperations to run in batch (max 10,000). The per-operation shape is provisional and finalised when the worker ships.
{
  "id": "b7e3c1a4-2f5d-4c0a-9e1b-8a7c3d2e1f00",
  "status": "queued"
}
GET/v1/batches/{batch_id}
60/min

Poll a batch job's status, request_counts, and results. Returns the lifecycle status (queued | running | completed | failed | cancelled | expired), running counts {total, completed, failed}, and — once status is 'completed' — the per-operation results from the results JSONB column. A job belonging to another account returns 404.

ParameterTypeRequiredDescription
batch_idstringrequiredUUID returned by POST /v1/batches.
{
  "id": "b7e3c1a4-2f5d-4c0a-9e1b-8a7c3d2e1f00",
  "status": "queued",
  "request_counts": { "total": 3, "completed": 0, "failed": 0 },
  "results": null,
  "created_at": "2026-05-27T12:00:00Z",
  "started_at": null,
  "finished_at": null,
  "expires_at": "2026-06-26T12:00:00Z"
}

MCP Server

The Scholar Feed MCP server exposes 23 tools as native MCP tools for Claude Code, Cursor, and Claude Desktop. Once installed, you can ask your AI assistant to search for papers, build co-author graphs, retrieve field orientations, and fetch full-text directly in your coding workflow.

Quick install

npx scholar-feed-mcp init

The init wizard detects your MCP client, prompts for your API key, writes the config file, and verifies the connection.

Manual configs

Claude Code

claude mcp add scholar-feed -e SF_API_KEY=sf_your_key_here -- npx -y scholar-feed-mcp

Cursor (.cursor/mcp.json)

{
  "mcpServers": {
    "scholar-feed": {
      "command": "npx",
      "args": ["-y", "scholar-feed-mcp"],
      "env": { "SF_API_KEY": "sf_your_key_here" }
    }
  }
}

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "scholar-feed": {
      "command": "npx",
      "args": ["-y", "scholar-feed-mcp"],
      "env": { "SF_API_KEY": "sf_your_key_here" }
    }
  }
}

Project-scoped (.mcp.json)

{
  "mcpServers": {
    "scholar-feed": {
      "command": "npx",
      "args": ["-y", "scholar-feed-mcp"],
      "env": { "SF_API_KEY": "${SF_API_KEY}" }
    }
  }
}

Uses ${SF_API_KEY} so the key comes from your environment, not the config file.

Windows

{
  "mcpServers": {
    "scholar-feed": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "scholar-feed-mcp"],
      "env": { "SF_API_KEY": "sf_your_key_here" }
    }
  }
}

Windows MCP clients require cmd /c npx to invoke Node.js correctly.

Available tools (8)

ToolDescription
search_papersSemantic + keyword search across 600k+ papers; also does similar-paper lookup (anchor_paper_id), citation-scoped search (scope_to_citations_of), and trending (sort='trending')
get_paperGet one or more papers by arXiv ID (arxiv_ids[]); supports batch up to 50 IDs and BibTeX export (format='bibtex')
get_citationsGet citation graph (incoming or outgoing) for a paper
fetch_fulltextExtract methodology / results / discussion sections from LaTeX source
find_authorFind researchers by topic or name (q=) or fetch a specific author profile (id=); merges discover + profile lookup
co_author_graphBuild a co-authorship graph for one or more authors; returns edges with paper counts and last-collaboration year
embed_textEmbed arbitrary text with Gemini Flash (768-dim); use for HyDE composition before passing to search_papers
get_field_orientationCheap retrieval of foundational papers and key sub-areas for a research field (no LLM synthesis; pairs with the /field-guide skill)

After setup, ask your assistant: “Search for recent papers on test-time compute scaling” or “Generate a field guide on reasoning in large language models”

Novelty Score

Every paper has an llm_novelty_score from 0.0 to 1.0, computed by DeepSeek using a three-component rubric (method novelty, demonstrated impact, scope of contribution). Use novelty_min: 0.5 in search to filter for genuinely novel work.

RangeMeaningExample
0.7+Paradigm shift or broad SOTANew architecture that changes the field
0.5–0.7Novel method with strong resultsNew training technique with clear gains
0.3–0.5Incremental improvementApplying known method to new domain
<0.3Survey, dataset, or minor extensionLiterature review, benchmark release

Errors

All errors return application/problem+json (RFC 9457) with a stable machine code and a request_id. The same request_id is returned in the X-Request-Id response header — include it when reporting an issue.

{
  "type": "about:blank",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Daily quota exceeded.",
  "instance": "/v1/papers/search",
  "code": "quota_exceeded",
  "request_id": "3f9a1b2c-...",
  "used": 1000,
  "limit": 1000,
  "resets": "2026-05-27T00:00:00+00:00",
  "contact_url": "https://www.scholarfeed.org/developers/need-more"
}
StatusCodeMeaning
401unauthorizedMissing or invalid API key
404not_foundRoute not found
422validation_errorMissing required params or wrong types (FastAPI RequestValidationError)
429rate_limitedPer-minute limit exceeded (slowapi). Retry-After = seconds until window resets.
429quota_exceededDaily (1,000/day) or monthly quota exhausted. Retry-After = seconds until midnight UTC.
429too_many_requestsPer-account concurrent-request cap. Retry-After = 1s (slot frees on completion).
429anon_daily_limitAnonymous IP daily cap (100/day). Retry-After = seconds until midnight UTC.
500internal_errorUnhandled server error; retry after a moment

Questions or issues? Open an issue on GitHub