Skip to content

Research Papers ​

Research adapters return ResearchPaper records: ordinary NewsItem fields (with kind: "analysis") plus a research object with authors, institution, series, DOI, JEL codes, categories, version, and PDF/license links when the upstream supplies them. You can call each source directly or let research providers join company and topic feeds.

xnews returns metadata and links. A pdfUrl is a pointer to the source, not a license to redistribute the paper.

Sources at a glance ​

Provider idStandalone APIKey neededNative searchPaging
arxivfetchArxivPapers, fetchArxivAnnouncementsNoarXiv query syntaxstart
openalexfetchOpenAlexWorksYes (apiKey)Full text, filters, sortcursor / nextCursor
bis-researchfetchBisWorkingPapersNoLocal filtering of the full snapshotNone
bis-research-hubfetchBisResearchHub, fetchBisResearchHubRecentNoquery, institutionsNone
nberfetchNberWorkingPapers, fetchNberRecentPapersNoqpage, perPage
ssrnfetchSsrnPapersNoNone (titles matched locally in feeds)index, count
crossreffetchCrossrefWorksNo (mailto recommended)Query, filters, select, sortcursor / nextCursor
world-bankfetchWorldBankDocumentsNoQuery, docTypes, languages, datesrows, offset
biorxiv, medrxivfetchBioRxivPapersNoDate window, categoriescursor
europe-pmcfetchEuropePmcPapersNoEurope PMC query languagecursorMark
hf-papersfetchHfDailyPapersNoNone (filtered locally in feeds)date
osf-preprintsfetchOsfPreprintsNoproviders, date boundsnextUrl

Every standalone function also has a pure parser (for example parseArxivPapers, parseCrossrefWorks) and, where the endpoint is URL-addressable, a URL builder in @xbbg/xnews/catalog.

Standalone discovery ​

ts
import {
  fetchArxivAnnouncements,
  fetchArxivPapers,
  fetchBisResearchHub,
  fetchBisWorkingPapers,
  fetchOpenAlexWorks,
} from "@xbbg/xnews";

const arxivTopic = await fetchArxivPapers('all:"monetary policy"', {
  sortBy: "submittedDate",
  sortOrder: "descending",
  limit: 10,
});
const arxivCategories = await fetchArxivAnnouncements(["econ.EM", "q-fin.EC"], { limit: 10 });

const openAlex = await fetchOpenAlexWorks("monetary policy", {
  apiKey: process.env.OPENALEX_API_KEY!,
  sort: "publication_date:desc",
  perPage: 10,
});

const bisWorkingPapers = await fetchBisWorkingPapers({ limit: 10 });
const researchHub = await fetchBisResearchHub({
  query: "inflation expectations",
  institutions: ["European Central Bank"],
  limit: 10,
});

console.log(
  arxivTopic[0]?.research.authors,
  arxivCategories[0]?.research.categories,
  openAlex.items[0]?.research.doi,
  bisWorkingPapers[0]?.research.issue,
  researchHub[0]?.research.institution,
);
  • fetchArxivPapers searches arXiv's legacy API with arXiv query syntax (all:, ti:, au:, cat:, boolean operators). fetchArxivAnnouncements reads the current announcement feed for one or more categories.
  • fetchOpenAlexWorks returns a page: items, count, nextCursor, and perPage.
  • fetchBisWorkingPapers reads the complete BIS Working Papers snapshot. fetchBisResearchHub searches the multi-institution Central Bank Research Hub snapshot; fetchBisResearchHubRecent reads its recent-additions RSS feed.

The remaining sources need no API key:

ts
import {
  fetchBioRxivPapers,
  fetchCrossrefWorks,
  fetchEuropePmcPapers,
  fetchHfDailyPapers,
  fetchNberWorkingPapers,
  fetchOsfPreprints,
  fetchSsrnPapers,
  fetchWorldBankDocuments,
} from "@xbbg/xnews";

const nber = await fetchNberWorkingPapers({ q: "inflation", limit: 10 });
const ssrn = await fetchSsrnPapers("fen", { limit: 10 }); // fen | arn | ern | numeric binding id
const crossref = await fetchCrossrefWorks("market liquidity", {
  filters: { type: "journal-article", "from-pub-date": "2026-01-01" },
  mailto: "research@example.com", // joins Crossref's polite pool
  limit: 10,
});
const prwp = await fetchWorldBankDocuments("inflation", {
  docTypes: ["Policy Research Working Paper"],
  limit: 10,
});
const medrxiv = await fetchBioRxivPapers({ server: "medrxiv", categories: ["endocrinology"] });
const biomed = await fetchEuropePmcPapers("glp-1 AND SRC:PPR", { resultType: "core", limit: 10 });
const aiPapers = await fetchHfDailyPapers({ date: "2026-08-06" });
const socarxiv = await fetchOsfPreprints({ providers: ["socarxiv"], publishedSince: "2026-08-01" });

console.log(nber.length, ssrn.length, crossref.totalResults, prwp.length);
console.log(medrxiv.length, biomed.hitCount, aiPapers.length, socarxiv.nextUrl);
  • NBER: fetchNberWorkingPapers uses the site's listing API (q, page, perPage) and includes abstracts; fetchNberRecentPapers reads the official new-papers RSS feed.
  • SSRN: fetchSsrnPapers lists a network's newest approved papers. SSRN_NETWORKS maps fen (Financial Economics), arn (Accounting Research), and ern (Economics Research) to binding ids; any numeric binding id also works. Listings have no abstracts.
  • Crossref: fetchCrossrefWorks exposes filters, select, sort/order, rows/offset, and cursor paging, and returns items, totalResults, and nextCursor.
  • World Bank: fetchWorldBankDocuments passes through Documents & Reports facets: docTypes, languages, since/until (dates), fields, and raw extraParams.
  • bioRxiv/medRxiv: fetchBioRxivPapers walks a date window (from/to, default the last 7 days) with cursor paging and optional category filtering. The upstream can take tens of seconds to answer.
  • Europe PMC: fetchEuropePmcPapers passes the Europe PMC query language through untouched and pages by cursorMark (nextCursorMark on the result).
  • Hugging Face: fetchHfDailyPapers lists the community-curated daily papers (arXiv-hosted) for a date.
  • OSF: fetchOsfPreprints filters OSF preprint servers (SocArXiv, PsyArXiv, and others) by provider and publication date, newest first, and returns nextUrl.

Research in company and topic feeds ​

All research providers are opt-in. Add them to sources and configure them with feed-level options:

ts
import { buildTopicNewsFeedResult } from "@xbbg/xnews";

const papers = await buildTopicNewsFeedResult({
  query: "inflation expectations",
  sources: ["arxiv", "openalex", "bis-research", "bis-research-hub", "nber", "crossref"],
  openAlexApiKey: process.env.OPENALEX_API_KEY!,
  arxivCategories: ["econ.EM", "q-fin"],
  bisInstitutions: ["European Central Bank"],
  crossrefFilters: { type: "journal-article" },
  since: "2026-01-01",
  limit: 40,
});

for (const paper of papers.items) {
  console.log(paper.provider, paper.publishedAt, paper.title, paper.research?.doi ?? "");
}
OptionApplies toEffect
openAlexApiKeyopenalexRequired. Without it the provider reports disabled
arxivCategoriesarxivLeaf ids (econ.EM) match exactly; archives (econ, q-fin) include every subcategory
bisInstitutionsbis-research, bis-research-hubExact institution names, for example "European Central Bank"
ssrnNetworksssrn"fen", "arn", "ern", or numeric binding ids; default ["fen"]
crossrefFilterscrossrefMerged into Crossref's filter parameter
worldBankDocTypesworld-bankRestricts document types
bioRxivCategoriesbiorxiv, medrxivRestricts subject categories
osfProvidersosf-preprintsRestricts OSF preprint servers, for example "socarxiv"

Sources without native search (bis-research, ssrn, biorxiv, medrxiv, hf-papers, osf-preprints) are fetched in bulk and filtered locally: every query token must appear in the title, summary, authors, institution, series, categories, or JEL codes (SSRN listings carry titles only). A date window is forwarded to arXiv (submitted date), OpenAlex, Crossref, Europe PMC (FIRST_PDATE), World Bank, bioRxiv/medRxiv, and OSF, and enforced locally for every provider.

When two research items share a canonical URL, the feed keeps one and records both providers in seenInProviders and provenance. Crossref, Europe PMC, bioRxiv/medRxiv, OSF, World Bank, and OpenAlex use https://doi.org/ plus the DOI as the canonical URL when a DOI is known, so the same paper from two of them can merge. Different URLs stay separate even when the metadata looks related; use the DOI or research.externalId when you need stronger cross-catalog deduplication (recipe below).

Recipes ​

Respect arXiv's request interval ​

arXiv asks legacy API clients to leave at least three seconds between consecutive requests and to use one connection at a time. xnews exports the interval but does not sleep for you:

ts
import { setTimeout as sleep } from "node:timers/promises";
import { ARXIV_MIN_REQUEST_INTERVAL_MS, fetchArxivPapers } from "@xbbg/xnews";

const queries = ['all:"yield curve"', 'all:"term premium"', "cat:q-fin.PR"];

for (const query of queries) {
  const papers = await fetchArxivPapers(query, {
    sortBy: "submittedDate",
    sortOrder: "descending",
    limit: 25,
  });
  console.log(query, papers.length);
  await sleep(ARXIV_MIN_REQUEST_INTERVAL_MS); // 3,000 ms
}

The same rule applies to feeds: a watchlist that includes arxiv sends one arXiv request per subject concurrently, so keep arXiv watchlists small or pace them.

Page through OpenAlex ​

ts
import { fetchOpenAlexWorks, OPENALEX_INITIAL_CURSOR, type ResearchPaper } from "@xbbg/xnews";

const collected: ResearchPaper[] = [];
let cursor: string | undefined = OPENALEX_INITIAL_CURSOR;

while (cursor !== undefined && collected.length < 500) {
  const page = await fetchOpenAlexWorks("central bank digital currency", {
    apiKey: process.env.OPENALEX_API_KEY!,
    filters: { from_publication_date: "2025-01-01" },
    perPage: 100,
    cursor,
  });
  collected.push(...page.items);
  cursor = page.items.length > 0 ? page.nextCursor : undefined;
}

OpenAlex requires an API key, meters each key against a daily budget, and returns rate-limit headers that are authoritative for short-term scheduling (PROVIDER_POLICIES.openalex). Crossref (cursor / nextCursor, starting from CROSSREF_INITIAL_CURSOR) and Europe PMC (cursorMark / nextCursorMark) page the same way.

Deduplicate across catalogs by DOI ​

ts
import { normalizeDoi, type ResearchPaper } from "@xbbg/xnews";

function paperKey(paper: ResearchPaper): string {
  const doi = paper.research.doi ? normalizeDoi(paper.research.doi) : undefined;
  return doi ? `doi:${doi}` : `url:${paper.canonicalUrl ?? paper.url}`;
}

function dedupePapers(papers: readonly ResearchPaper[]): ResearchPaper[] {
  const byKey = new Map<string, ResearchPaper>();
  for (const paper of papers) {
    if (!byKey.has(paperKey(paper))) byKey.set(paperKey(paper), paper);
  }
  return [...byKey.values()];
}

normalizeDoi returns the bare, lower-cased 10.x/y form. research.externalId (arXiv id, OpenAlex id, and so on) is another stable key within one catalog.

The research metadata ​

FieldMeaning
authorsAuthor names as supplied
institution, countryPublishing institution and its country, when the source states them (for example BIS and Research Hub records)
series, issueWorking-paper series and number
doiDOI, when known
jelCodesJEL classification codes
categoriesSubject categories (arXiv categories, bioRxiv subjects, and so on)
externalIdThe catalog's own identifier
versionVersion label (arXiv v2)
submittedAt, updatedAt, announcedAt, announceTypeLifecycle dates and announcement type, when supplied
pdfUrl, licenseUrlSource links. xnews does not download or redistribute the paper

Every field is optional. See Data Model for the surrounding NewsItem fields.

Usage policies ​

  • arXiv: at least three seconds between legacy API requests (ARXIV_MIN_REQUEST_INTERVAL_MS), one connection at a time. API metadata is CC0, but PDFs and source files are not generally redistributable unless the paper's license permits it. Link to arXiv by default.
  • OpenAlex: the adapter requires apiKey; see OpenAlex authentication and pricing. Data is CC0, but budgets and rate limits apply; honor the response headers.
  • BIS and Research Hub: metadata and source links only. RePEc indexing or public download access does not grant commercial redistribution rights. Review the BIS terms and request permission where your use requires it.
  • NBER and SSRN: both listing endpoints are the sites' own undocumented APIs (NBER's RSS feed is the stable official surface) and may change without notice. Both return metadata and landing links only.
  • Crossref and World Bank: free public APIs. Pass mailto to join Crossref's polite pool; abstracts are publisher-supplied and may carry publisher rights. Most World Bank publications are CC BY 4.0, but each record's license statement is authoritative.
  • bioRxiv/medRxiv, Europe PMC, and OSF: free, keyless APIs with per-record content licenses; only the metadata is openly reusable across the board. OSF throttles unauthenticated traffic.
  • Hugging Face daily papers: an undocumented but widely used endpoint over community-curated arXiv listings; treat availability and shape as best-effort.

The same facts are machine-readable in PROVIDER_POLICIES (Provider Policies).

xbbg is independent open-source software and is not affiliated with, endorsed by, sponsored by, or approved by Bloomberg Finance L.P.