---
title: "V1 Search Papers"
method: GET
path: "/v1/papers/search"
tags: ["v1"]
---

# V1 Search Papers

`GET /v1/papers/search`

## Request

- Base URL: `https://api.scholarfeed.org` (the document declares no server; this is the origin it was published from)
- URL: `https://api.scholarfeed.org/v1/papers/search`
- Auth: HTTP bearer

Search papers by keyword or semantic similarity with optional filters.

v3 extended params (TOOL-03):
- anchor_paper_id: similar-papers mode (absorbs find_similar). Results ordered by
  similarity_score DESC using precomputed related_papers neighbors. q is ignored.
- scope_to_citations_of: citation-scoped search (absorbs find_citations_about).
  Takes the citation neighborhood of the given paper, embeds q, ranks by cosine
  similarity. Results carry similarity_score.
- sort: relevance↔impact dial + ordering knob. 'relevance' (default), 'balanced'
  (relevant + well-cited), 'impactful' (proven citations among the relevant) rerank
  the semantic pool by pool-relative log-citations; 'trending' = impact_pct DESC
  (rising/forecast, ~90d), 'recent' = published_date DESC. In keyword mode the dial
  degrades: 'impactful' = citation_count DESC, 'balanced' = rank_score (default).

Param precedence: anchor_paper_id > scope_to_citations_of > sort.
sort= is silently ignored when anchor_paper_id or scope_to_citations_of is set.

Query-less browse: sort=trending/recent/impactful may be called with NO q (the
topic-free "what's rising right now" feed). Those three order by an indexed
column, so the planner early-stops at LIMIT. Capped to the first 200 results and
returns total=null (an unfiltered COUNT(*) scans the whole corpus). sort=community
is excluded on purpose: its NULLS LAST ordering has no usable index and degrades
to a full seq scan. Every other q-less request still 422s.

Modes (keyword/semantic):
- keyword (default): PostgreSQL full-text search. Fast, exact matches.
- semantic: Gemini embedding similarity. Finds conceptually related papers
  even without exact keyword matches. Slower (requires Gemini API call).

All structured filters (category, novelty_min, days) work with both keyword
and semantic modes. Filters are not applied in anchor mode.

Rate limit: 30/minute per API key (or IP for JWT users).

## Query parameters

- `q` string, nullable: Search query. Optional when anchor_paper_id is set (ignored in anchor mode), and when sort is one of trending/recent/impactful (query-less 'browse the frontier' feed, capped to the first 200 results and served on the keyword path). Required otherwise.
- `category` string, nullable: Filter by category code e.g. 'cs.AI'
- `novelty_min` number, nullable: Minimum llm_novelty_score
- `impact_min` integer, nullable: Minimum impact_pct (0-100): the ML forecaster's per-category percentile of predicted 12-month impact. e.g. 80 = top 20%. Unscored papers (NULL impact_pct) are excluded.
- `has_code` boolean, nullable: Factual filter: when true, restrict to papers with a known code release (has_code = TRUE).
- `min_citations` integer, nullable: Factual filter: minimum citation_count (NULLs treated as 0, so unscored papers are excluded by any positive threshold).
- `github_url_exists` boolean, nullable: Factual filter: when true, restrict to papers that carry a GitHub repo URL (github_url IS NOT NULL).
- `min_stars` integer, nullable: Factual filter: papers KNOWN to have at least this many github_stars. Requires a measured star count, so papers whose repo was never fetched are excluded rather than counted as 0-star — honest but incomplete: a genuinely popular paper can be absent because nobody measured it. Star coverage skews toward recently-published papers and is being backfilled.
- `published_after` string, date, nullable: Factual filter: only papers published on/after this date (YYYY-MM-DD), inclusive — published_date >= :published_after. Malformed/invalid dates are rejected with 422.
- `published_before` string, date, nullable: Factual filter: only papers published on/before this date (YYYY-MM-DD), inclusive — published_date <= :published_before. Malformed/invalid dates are rejected with 422.
- `days` integer, nullable: Limit to papers published within N days
- `mode` string, nullable: Search mode: 'keyword' (default, full-text search) or 'semantic' (embedding similarity). Semantic mode finds conceptually related papers even without exact keyword matches.
- `rerank` boolean: Semantic-mode only: apply summary-aware intent reranking on the top-100 candidate pool. Default true. Pass false to get raw cosine ordering (matches the pre-2026-05-24 baseline).
- `hyde` boolean: Semantic-mode only: generate a hypothetical paper abstract (DeepSeek) and blend its embedding with the query embedding for retrieval. Lifts recall on descriptive queries (Q4 in eval: 20%→60%). Adds ~5-10s on cache miss; cached 24h. Off by default while we validate prod latency.
- `sort` string, nullable: Sort/rank order. The relevance↔impact dial (semantic mode): 'relevance' (default, best match), 'balanced' (relevant AND well-cited), 'impactful' (most-cited among the relevant — proven influence; in keyword mode = citation_count DESC). Plus 'trending' (rising/forecast: impact_pct DESC, last ~90d), 'recent' (published_date DESC), and 'community' (traction: github_stars DESC, then citation_velocity DESC; applied in BOTH keyword and semantic mode, where it reorders the cosine candidate pool. Papers with no measured star count sink below genuine zeros instead of tying with them — a 0 means 'unknown', so this is a measured-adoption order, not a corpus-wide one). 'balanced'/'impactful' rerank the cosine candidate pool by pool-relative log-citations. Ignored when anchor_paper_id or scope_to_citations_of is set.
- `anchor_paper_id` string, nullable: Return papers similar to this arXiv paper (precomputed related_papers neighbors). When set, results carry similarity_score and are ordered by similarity DESC. q is ignored in this mode. Absorbs find_similar(arxiv_id) from v1.8.0.
- `scope_to_citations_of` string, nullable: Restrict search to papers in this arXiv paper's citation graph, then rank by relevance to q. Results carry similarity_score. sort= is ignored; ordering is cosine DESC. Absorbs find_citations_about(arxiv_id, query) from v1.8.0.
- `cursor` string, nullable: Cursor for keyset pagination (from next_cursor in previous response)
- `page` integer
- `limit` integer
- `fields` string, nullable: Comma-separated list of fields to return (e.g. 'arxiv_id,title,llm_summary'). If omitted, returns the lean 12-field default unless verbose=true.
- `verbose` boolean: If true, returns the full 28-field paper shape. Default false returns the lean set. Ignored when `fields` is provided.
- `exclude_ids` string, nullable: Comma-separated arXiv IDs to exclude from results (max 100)

## Response `200`

Successful Response

- object

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/scholarfeed/apis/scholar-feed-api.md) · [All operations](https://skmtc.dev/scholarfeed/apis/scholar-feed-api/llms.txt) · [OpenAPI document](https://skmtc.dev/scholarfeed/apis/scholar-feed-api/revisions/fc3200d27505?raw)
