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. Get an API key
Generate a key at the API keys page. Keys start with
sf_.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. Or install the MCP server for Claude Code / Cursor
npx scholar-feed-mcp initThe 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/v1Legacy 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.jsonRate 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.
| Method | Endpoint | Per-min |
|---|---|---|
| GET | /v1/health | 60/min |
| GET | /v1/papers/search | 30/min |
| GET | /v1/papers | 30/min |
| GET | /v1/papers/{arxiv_id} | 60/min |
| GET | /v1/papers/{arxiv_id}/similar | 20/min |
| GET | /v1/papers/{arxiv_id}/citations | 30/min |
| GET | /v1/papers/{arxiv_id}/citations/about | 20/min |
| GET | /v1/papers/{arxiv_id}/fulltext | 10/min |
| GET | /v1/papers/{arxiv_id}/repo | 10/min |
| POST | /v1/papers/bibtex | 20/min |
| GET | /v1/trending | 30/min |
| GET | /v1/authors/discover | 20/min |
| GET | /v1/authors/co-author-graph | 20/min |
| GET | /v1/authors/{author_id} | 30/min |
| GET | /v1/authors/{author_id}/papers | 20/min |
| GET | /v1/research/landscape | 10/min |
| POST | /v1/embeddings | 30/min |
| GET | /v1/field-orientation | 20/min |
| POST | /v1/batches | 10/min |
| GET | /v1/batches/{batch_id} | 60/min |
Endpoints
/v1/healthConnection 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
/v1/papers/searchFull-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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | required | Search query (supports phrase and boolean operators) |
| category | string | optional | Filter by arXiv category, e.g. "cs.AI", "cs.LG", "stat.ML" |
| novelty_min | float | optional | Minimum llm_novelty_score (0.0–1.0). Use 0.5 to filter for genuinely novel work. |
| days | int | optional | Restrict to papers published within the last N days |
| sort | string | optional | "rank" (default) or "recent" or "trending" |
| anchor_paper_id | string | optional | arXiv ID of a seed paper; results are semantically similar papers |
| scope_to_citations_of | string | optional | Restrict search to papers that cite the given arXiv ID |
| limit | int | optional | Results per page (default 20, max 50) |
| cursor | string | optional | Keyset pagination cursor from next_cursor in a previous response |
| fields | string | optional | Comma-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=="
}/v1/papersBatch 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_ids[] | string | required | Repeated arXiv IDs to batch-fetch, e.g. ?arxiv_ids[]=2401.1&arxiv_ids[]=2402.2 (max 50). Version suffixes stripped. |
| fields | string | optional | Comma-separated fields to return. Default: lean 12-field set. |
| verbose | bool | optional | If 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"]
}/v1/papers/{arxiv_id}Retrieve a single paper by arXiv ID. Version suffixes are stripped automatically (e.g. 2401.12345v2 → 2401.12345).
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_id | string | required | arXiv paper ID, e.g. "2401.04088" or "2401.04088v2" |
| fields | string | optional | Comma-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"
}/v1/papers/{arxiv_id}/similarFind papers similar to the given paper using precomputed neighbors (65% embedding cosine similarity + 25% bibliographic coupling + 10% direct-citation bonus).
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_id | string | required | arXiv paper ID of the source paper |
| limit | int | optional | Number of similar papers to return (default 10, max 30) |
| days | int | optional | Restrict similar papers to those published within N days |
| fields | string | optional | Comma-separated fields to return |
{
"papers": [
{
"arxiv_id": "2312.08901",
"title": "Efficient Sparse Transformers for Long Sequences",
"similarity_score": 0.87
}
],
"total": 10
}/v1/papers/{arxiv_id}/citationsGet the citation graph for a paper. direction='cited_by' returns papers that cite this paper (incoming); direction='citing' returns papers this paper cites (outgoing).
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_id | string | required | arXiv paper ID |
| direction | string | optional | "cited_by" (default) or "citing" |
| limit | int | optional | Number of papers to return (default 20, max 50) |
| fields | string | optional | Comma-separated fields to return |
{
"papers": [
{
"arxiv_id": "2403.11234",
"title": "Scaling Laws for Sparse Attention"
}
],
"total": 15,
"direction": "cited_by"
}/v1/papers/{arxiv_id}/citations/aboutFind 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_id | string | required | arXiv paper ID |
| query | string | required | Natural-language topic to filter the citation graph by (2–300 chars) |
| direction | string | optional | "cited_by" (default) or "citing" |
| limit | int | optional | Number of papers to return (default 20, max 50) |
| fields | string | optional | Comma-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"
}/v1/papers/{arxiv_id}/fulltextExtract 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_id | string | required | arXiv 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"
}/v1/papers/{arxiv_id}/repoFetch 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_id | string | required | arXiv 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
}/v1/papers/bibtexExport BibTeX entries for up to 50 arXiv papers. Returns a single BibTeX string with all found entries concatenated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| arxiv_ids | string[] | required | Array 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
/v1/trendingGet 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| category | string | optional | Filter by arXiv category, e.g. "cs.AI" |
| limit | int | optional | Number of papers to return (default 20, max 50) |
| fields | string | optional | Comma-separated fields to return |
{
"papers": [
{
"arxiv_id": "2503.12345",
"title": "State Space Models Strike Back",
"rank_score": 0.91
}
],
"total": 20
}Authors
/v1/authors/discoverDiscover 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | required | Topic or researcher name to search (2–500 chars) |
| field | string | optional | Filter by primary field, e.g. "cs.LG" |
| limit | int | optional | Number 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
}/v1/authors/co-author-graphCo-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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| author_ids | string | required | Comma-separated author IDs (max 25), e.g. "12345,67890" |
| window_years | int | optional | Only 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
}/v1/authors/{author_id}Get author details by ID. Returns author profile with h-index, rank, top papers, and research topics. Use discover to find author IDs first.
| Parameter | Type | Required | Description |
|---|---|---|---|
| author_id | int | required | Numeric 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
}
]
}/v1/authors/{author_id}/papersGet all papers by an author, sorted by rank_score. Returns paginated PublicPaperResponse list.
| Parameter | Type | Required | Description |
|---|---|---|---|
| author_id | int | required | Numeric author ID |
| limit | int | optional | Results per page (default 20, max 50) |
| page | int | optional | Page 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
/v1/research/landscapeAggregated 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| q | string | required | Research topic (semantic search, 2–500 chars) |
| limit | int | optional | Number 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
/v1/embeddingsEmbed 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| text | string | required | Text to embed (1–8000 chars) |
| task_type | string | optional | "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"
}/v1/field-orientationCheap 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| topic | string | required | Research area to orient on (5–300 chars), e.g. "diffusion models for protein structure" |
| limit | int | optional | Number 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
/v1/batchesSubmit 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| requests | object[] | required | Operations 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"
}/v1/batches/{batch_id}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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| batch_id | string | required | UUID 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 initThe 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-mcpCursor (.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)
| Tool | Description |
|---|---|
| search_papers | Semantic + 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_paper | Get one or more papers by arXiv ID (arxiv_ids[]); supports batch up to 50 IDs and BibTeX export (format='bibtex') |
| get_citations | Get citation graph (incoming or outgoing) for a paper |
| fetch_fulltext | Extract methodology / results / discussion sections from LaTeX source |
| find_author | Find researchers by topic or name (q=) or fetch a specific author profile (id=); merges discover + profile lookup |
| co_author_graph | Build a co-authorship graph for one or more authors; returns edges with paper counts and last-collaboration year |
| embed_text | Embed arbitrary text with Gemini Flash (768-dim); use for HyDE composition before passing to search_papers |
| get_field_orientation | Cheap 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.
| Range | Meaning | Example |
|---|---|---|
| 0.7+ | Paradigm shift or broad SOTA | New architecture that changes the field |
| 0.5–0.7 | Novel method with strong results | New training technique with clear gains |
| 0.3–0.5 | Incremental improvement | Applying known method to new domain |
| <0.3 | Survey, dataset, or minor extension | Literature 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"
}| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing or invalid API key |
| 404 | not_found | Route not found |
| 422 | validation_error | Missing required params or wrong types (FastAPI RequestValidationError) |
| 429 | rate_limited | Per-minute limit exceeded (slowapi). Retry-After = seconds until window resets. |
| 429 | quota_exceeded | Daily (1,000/day) or monthly quota exhausted. Retry-After = seconds until midnight UTC. |
| 429 | too_many_requests | Per-account concurrent-request cap. Retry-After = 1s (slot frees on completion). |
| 429 | anon_daily_limit | Anonymous IP daily cap (100/day). Retry-After = seconds until midnight UTC. |
| 500 | internal_error | Unhandled server error; retry after a moment |
Questions or issues? Open an issue on GitHub