{"openapi": "3.1.0", "info": {"title": "DoxaLibera", "version": "0.1.0", "x-logo": {"url": "/favicon.svg", "altText": "DoxaLibera"}}, "tags": [{"name": "Papers", "description": "Get a paper and its numbers"}, {"name": "Service", "description": "Is it up? (no key needed)"}], "security": [{"bearer": []}], "paths": {"/papers": {"get": {"tags": ["Papers"], "operationId": "getPaper", "summary": "Get a paper: the PDF (default) or its metadata", "description": "Sources, in order: the archive, Sci-Hub, arXiv, legal open-access copies (OpenAlex). Titles and PMIDs are turned into DOIs first. With every paper come its citations and journal ranking, in the `X-Doxa-*` headers or in `metrics`.\n\n**Timing.** An archived paper comes back at once; a new one is downloaded while the request waits, 3-60 s: give clients a timeout of at least 120 s.\n\n**Errors** are JSON with `reason`, `error` and a `hint` saying what to do. Retry only `502` and `503`, after their `Retry-After`.", "parameters": [{"name": "id", "in": "query", "required": true, "schema": {"type": "string"}, "description": "DOI (`10.1038/...`, `doi:...`, a doi.org link), PMID, arXiv ID (`arXiv:1706.03762`, an arxiv.org link) or the paper's title", "examples": {"doi": {"summary": "DOI", "value": "10.1145/3065386"}, "pmid": {"summary": "PMID", "value": "12477932"}, "arxiv": {"summary": "arXiv ID", "value": "arXiv:1706.03762"}, "title": {"summary": "Title", "value": "ImageNet Classification with Deep Convolutional Neural Networks"}}}, {"name": "format", "in": "query", "schema": {"type": "string", "enum": ["pdf", "json"], "default": "pdf"}, "description": "`pdf`: the file. `json`: its metadata and a `pdf_url` instead."}, {"name": "metrics", "in": "query", "schema": {"type": "boolean", "default": true}, "description": "Citations and journal ranking from OpenAlex. `false` skips them (a little faster). Unknown values are left out."}, {"name": "refs", "in": "query", "schema": {"type": "boolean", "default": false}, "description": "Also list the DOIs the paper cites. Needs `format=json`."}, {"name": "force", "in": "query", "schema": {"type": "boolean", "default": false}, "description": "Download again even if archived."}], "responses": {"200": {"description": "The paper", "headers": {"X-Doxa-Status": {"description": "`ok` (downloaded now) or `present` (already archived)", "schema": {"type": "string", "enum": ["ok", "present"]}}, "X-Doxa-Doi": {"description": "The DOI the paper was found under (percent-encoded)", "schema": {"type": "string"}}, "X-Doxa-Title": {"description": "Title, percent-encoded UTF-8: decode it (`urllib.parse.unquote`, `decodeURIComponent`)", "schema": {"type": "string"}}, "X-Doxa-Author": {"description": "\"Surname\" or \"Surname et al.\", percent-encoded UTF-8", "schema": {"type": "string"}}, "X-Doxa-Year": {"schema": {"type": "string"}}, "X-Doxa-Source": {"description": "URL the PDF came from (percent-encoded)", "schema": {"type": "string"}}, "X-Doxa-Open-Access": {"description": "`true` when it is a legal open-access copy (OpenAlex)", "schema": {"type": "string", "enum": ["true", "false"]}}, "Content-Disposition": {"description": "attachment; filename*=UTF-8''<Author Year - Title - DOI>.pdf", "schema": {"type": "string"}}, "X-Doxa-Citations": {"description": "Times the paper was cited (OpenAlex)", "schema": {"type": "integer"}}, "X-Doxa-Fwci": {"description": "Field-weighted citation impact: citations relative to papers of the same field and year, 1 = average", "schema": {"type": "number"}}, "X-Doxa-Citation-Percentile": {"description": "0-100, rank by citations within field and year", "schema": {"type": "number"}}, "X-Doxa-Retracted": {"schema": {"type": "string", "enum": ["true", "false"]}}, "X-Doxa-Journal": {"description": "Journal or venue, percent-encoded UTF-8", "schema": {"type": "string"}}, "X-Doxa-Issn": {"schema": {"type": "string"}}, "X-Doxa-Publisher": {"description": "Percent-encoded UTF-8", "schema": {"type": "string"}}, "X-Doxa-Journal-H-Index": {"schema": {"type": "integer"}}, "X-Doxa-Journal-2yr-Mean-Citedness": {"description": "Citations per paper over two years, close to an impact factor", "schema": {"type": "number"}}}, "content": {"application/pdf": {"schema": {"type": "string", "format": "binary"}}, "application/json": {"schema": {"$ref": "#/components/schemas/Paper"}, "example": {"status": "present", "id": "10.1145/3065386", "doi": "10.1145/3065386", "resolved_from": "", "author": "Krizhevsky et al.", "year": "2017", "title": "ImageNet classification with deep convolutional neural networks", "source": "https://sci-hub.su/storage/2024/6364/d71033ef8ee149f894a1f173a0f662b0/krizhevsky2017.pdf", "open_access": false, "file": "Krizhevsky et al. 2017 - ImageNet classification with deep convolutional neural networks - 10.1145_3065386.pdf", "pdf_url": "/files/Krizhevsky%20et%20al.%202017%20-%20ImageNet%20classification%20with%20deep%20convolutional%20neural%20networks%20-%2010.1145_3065386.pdf", "refs": [], "metrics": {"citations": 109560, "fwci": 5039.88, "citation_percentile": 100.0, "retracted": false, "journal": "Communications of the ACM", "issn": "0001-0782", "publisher": "Association for Computing Machinery", "journal_h_index": 438, "journal_2yr_mean_citedness": 4.47}}}}}, "400": {"description": "Bad request: missing `id`, unknown `format`, `refs` without `format=json`. Fix it, don't retry.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Failure"}}}}, "401": {"description": "Missing or wrong API key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Failure"}}}}, "404": {"description": "`no_match`: no title close enough. `missing`: no source has it. Retrying won't help.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Failure"}, "examples": {"no_match": {"value": {"status": "failed", "reason": "no_match", "error": "no Crossref or arXiv title close enough", "hint": "no paper has a title close enough: send its DOI instead", "id": "10.1234/example", "doi": "10.1234/example"}}, "missing": {"value": {"status": "failed", "reason": "missing", "error": "Article not on sci-hub: 10.1234/example", "hint": "no source has this paper; retrying soon will not help", "id": "10.1234/example", "doi": "10.1234/example"}}}}}}, "502": {"description": "`error`: the download failed for now (mirrors or VPN exits down, disk almost full). Retry after `Retry-After`.", "headers": {"Retry-After": {"description": "Seconds to wait before retrying", "schema": {"type": "integer"}}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Failure"}, "example": {"status": "failed", "reason": "error", "error": "every exit is resting (e.g. NordVPN refused the account for too many connections): try again in a few minutes", "hint": "temporary: retry in a few minutes (see Retry-After)", "id": "10.1234/example", "doi": "10.1234/example"}}}}, "503": {"description": "Too many connections at once: retry after `Retry-After`.", "headers": {"Retry-After": {"description": "Seconds to wait before retrying", "schema": {"type": "integer"}}}}}}}, "/files/{name}": {"get": {"tags": ["Papers"], "operationId": "getFile", "summary": "An archived PDF by file name (the `pdf_url` of a JSON answer)", "parameters": [{"name": "name", "in": "path", "required": true, "schema": {"type": "string"}}], "responses": {"200": {"description": "The PDF", "content": {"application/pdf": {"schema": {"type": "string", "format": "binary"}}}}, "401": {"description": "Missing or wrong API key"}, "404": {"description": "No such file"}}}}, "/health": {"get": {"tags": ["Service"], "operationId": "health", "summary": "Is the service up, and which version?", "security": [], "responses": {"200": {"description": "Up", "content": {"application/json": {"schema": {"type": "object", "properties": {"ok": {"type": "boolean"}, "version": {"type": "string"}, "uptime_s": {"type": "integer", "description": "Seconds since the service started"}}}, "example": {"ok": true, "version": "0.1.0", "uptime_s": 3600}}}}}}}, "/stats": {"get": {"tags": ["Service"], "operationId": "stats", "summary": "What the service has been doing since it started", "description": "Requests by outcome, downloads by source, timings, clients, the latest requests and problems, the archive and the network. What the terminal dashboard (`doxalibera --dashboard`) shows. No key needed from the machine itself.", "responses": {"200": {"description": "Counters since the last restart", "content": {"application/json": {"schema": {"type": "object"}}}}, "401": {"description": "Missing or wrong API key"}}}}}, "components": {"securitySchemes": {"bearer": {"type": "http", "scheme": "bearer", "description": "The service's API key (DOXALIBERA_API_KEY)"}}, "schemas": {"Paper": {"type": "object", "properties": {"status": {"type": "string", "enum": ["ok", "present"], "description": "`ok` downloaded now, `present` already archived"}, "id": {"type": "string", "description": "The identifier as sent"}, "doi": {"type": "string", "description": "The DOI it resolved to"}, "resolved_from": {"type": "string", "enum": ["", "title", "pmid"], "description": "Set when the DOI was looked up from a title or PMID"}, "author": {"type": "string"}, "year": {"type": "string"}, "title": {"type": "string"}, "source": {"type": "string", "description": "URL the PDF came from"}, "open_access": {"type": "boolean"}, "file": {"type": "string"}, "pdf_url": {"type": "string", "description": "Path of the PDF on this service"}, "refs": {"type": "array", "items": {"type": "string"}, "description": "Cited DOIs, with refs=true"}, "metrics": {"$ref": "#/components/schemas/Metrics"}}}, "Metrics": {"type": "object", "description": "From OpenAlex; empty when unknown (e.g. arXiv preprints), null fields when missing.", "properties": {"citations": {"type": ["integer", "null"]}, "fwci": {"type": ["number", "null"], "description": "1 = average for field and year"}, "citation_percentile": {"type": ["number", "null"], "description": "0-100"}, "retracted": {"type": "boolean"}, "journal": {"type": "string"}, "issn": {"type": "string"}, "publisher": {"type": "string"}, "journal_h_index": {"type": ["integer", "null"]}, "journal_2yr_mean_citedness": {"type": ["number", "null"]}}}, "Failure": {"type": "object", "properties": {"status": {"type": "string", "enum": ["failed"]}, "reason": {"type": "string", "enum": ["no_match", "missing", "error"]}, "error": {"type": "string", "description": "What went wrong"}, "hint": {"type": "string", "description": "What to do about it"}, "id": {"type": "string"}, "doi": {"type": "string"}}}}}}