1 · What this is, and is not
CiteLink’s own pages read their data from a public PostgREST endpoint using a read-only key shipped in assets/js/data.js. Anyone who opened the developer tools already had it. This page documents those routes, because our development plan holds that “belgesiz bir iç API kamuya açık sayılmaz” — an undocumented internal API does not count as public.
That principle cuts both ways, and the second half matters more than the first:
- Everything listed here is documented, and we will not remove it without notice on this page.
- Anything reachable but not listed here is not an interface. It is an internal detail that happens to answer, and the correct response to finding one is for us to document it or close it — which is what we did in August 2026 when an archive table was found answering that no page read.
/v1. The column sets below are shaped for this site’s own pages and change when a page changes — twice in one week in August 2026, both times silently breaking a cached consumer. Versioning is a promise about the future and we are not in a position to keep it, so we are not making it. If you build on this, pin nothing and re-read the shape you get.This page is generated by hand but not maintained by hand: every route named below is called on every commit by tools/check-routes.js, and a name that stops answering fails the build. That is the only reason to believe a page like this.
Two exceptions, added 19 September 2026, and they are named here rather than left for you to discover. That gate reads this page’s own data-route markup and calls each name against the PostgREST base in §2. The two routes at the foot of §5 — /api/extra/{id} and /api/view/{id} — are not PostgREST and carry no such marking, so they are documented here without being checked here. One of them writes, which is the other reason a build-time gate does not call it. Everything else on this page is checked.
2 · Base URL
The interface is PostgREST in front of PostgreSQL 17, on CiteLink’s own server in Berlin. One base URL, and nothing else to send:
Base URL https://www.citelink.org/rest/v1/
There is no key any more, and that is a change from what this page said before. Until 18 September 2026 the API lived on Supabase, where every request carried an apikey header and an identical Authorization: Bearer header, and this page published that key. On 18 September 2026 the database moved to CiteLink’s own server and the API moved with it; PostgREST here runs with no token at all, so those headers are not needed and the published key no longer opens anything. §2 promised the key would not be removed without a note on this page, and this paragraph is that note. Requests that still send the old headers are answered normally — they are ignored, not rejected — so anything written against the old address needs only its host changed.
The key grants the anon role, which holds SELECT on the 24 relations in §4 and EXECUTE on the functions in §5. It holds no write privilege on anything you can read.
That sentence was false when this page was published on 3 September 2026, and an adversarial check of the page caught it rather than a reader. Three write privileges nobody had meant to grant were sitting under it: author_facets_mv carried INSERT, UPDATE, DELETE and TRUNCATE for this role, and catalog_sig_refresh() and catalog_sig_touch() were executable by everyone. None of the three could be used — each was stopped by a different accident, and not one of them by an intended grant — so the page stated the exception instead of the sentence, which was honest and worse than not needing to. They were revoked on 11 September 2026 (etl/v11/close-anon-writes.sql), and the sentence is true again. The read on author_facets_mv was deliberately kept; the three intake tables in §6 still take INSERT and refuse SELECT, which is what they are for; and the trigger on journals still fires, measured against the live trigger rather than assumed, because PostgreSQL checks EXECUTE when a trigger is created and not when it runs.
CORS is open. A preflight from an unrelated origin returns Access-Control-Allow-Origin: *, so a page on your own domain can call these routes directly. That is the difference between “usable from a notebook” and “usable from a web page”, and both work.
The API describes itself, as of 18 September 2026. GET /rest/v1/ returns PostgREST’s own OpenAPI document, and it follows the grants — it lists what anon may actually reach, not everything that exists. This page said the opposite until that date, and the reason was true while it lasted: Supabase answered that route with 401, “Only the service_role API key can be used for this endpoint”, so a machine-readable schema was not available at all. It is now. This page remains the one written for people — what a column means, what it does not mean, and which figures are derived — which a schema cannot tell you.
3 · Limits, measured
Each of these was measured against the live instance, not read off a plan.
| Limit | Value | How it was established |
|---|---|---|
| Statement timeout | 8 seconds | statement_timeout=8s on the authenticator and authenticated roles in pg_db_role_setting; anon itself carries no setting, and an anonymous request runs inside an authenticator session. Over the wire: one author search returned 500 with 57014 canceling statement due to statement timeout at 8,635 ms. |
| Rows per response | 1,000 | PostgREST db-max-rows. GET /journals?select=id&limit=100000 returns 200 with exactly 1,000 rows — no error, no warning. Page with limit and offset. |
| True result size | Content-Range |
Send Prefer: count=exact. On journals that returns 0-0/58392, which is the whole catalogue and not the page you were given. |
| Rate limit | none configured | No limiter is configured and none was observed. Read the next paragraph before treating that as permission. |
One route still crosses the ceiling for a handful of names, and it used to do it for many. rpc/search_authors failed for nine of sixty-four test terms on an idle instance until 5 September 2026; it now fails for one. The cause was not the size of the table but a blind query planner: this table carries a row-security policy that grants everyone access to every row, and the presence of that policy is enough to stop the planner reading its own statistics for a substring match. It fell back to guessing from the length of what you typed. The guess was wrong in the expensive direction, and worst for the rarest names — ayvaz, which matches 58 people in the whole table, took over two minutes, and three-letter strings matching almost nothing failed too. Turkish surnames were hit hardest. Two changes fixed it: the statistics on the name index were made ten times finer, because at the old resolution a surname held by 0.57% of authors did not appear in the sample at all and was estimated at 737 rows against 42,101; and the search now plans with those statistics visible to it. What remains is a memory limit, not a planning one: a search matching tens of thousands of authors has to read tens of thousands of scattered pages, and this instance can cache only a fifth of the table, so the same query can take two seconds or seven depending on what happens to be in memory. That is listed in §5 rather than left out, because a list that quietly omits the flaky route is worse than one that names it.
4 · Tables and views
All 24 relations the anonymous key can read, each fetched to confirm it answers. Call them as GET /rest/v1/{name}?select=*&limit=1 and filter with PostgREST’s operators.
Ten of them are read directly by this site’s pages. The other fourteen answer and no page fetches them by name; they are listed anyway, because under the principle in §1 the alternative to documenting a reachable route is closing it.
| Route | Kind | What it holds |
|---|---|---|
journals | table | The catalogue. 63,664 records covering 62,643 journals — a split record is two rows. Title, ISSN, publisher, country, open-access flags, index-coverage flags, best_quartile. |
articles | table | 104,664,791 articles with year, citations, CNI and percentile, and an abstract column carrying OpenAlex’s abstract text on 63,484,528 rows — 60.66%, counted on the server on 19 September 2026, and a figure this page stores rather than reads, because an exact count over that column takes about ninety seconds and the anonymous role is cut off at eight. This row said 7,167,968 until 19 September 2026: the article layer grew when the site moved to its own server, and the abstract column came with it. §8 gives the abstract its own licence line; the CC0 row there does not cover it. |
authors | table | 7,369,024 author records: name, ORCID, works and citation counts. |
organizations | table | 126,410 institutions: name, ROR, country, type. |
journal_authors | table | Journal–author edges with a per-journal rank. works_n counts authorships, not distinct articles. |
author_articles | table | Author–article edges with the author’s position. |
article_language | table | Article language, carrying every sort key in its own row so a language filter does not have to probe back into articles. |
source_fields | table | The field and domain label attached to a journal, with the share of scanned works it rests on. |
cl_metrics | table | Computed journal metrics: CLIF2/CLIF5, CNI, immediacy, h, SNIP-open, self-citation ratio. Carries metric_version. |
cl_quartiles | table | 47,482 subfield ranking rows: pp10 with a bootstrap interval, rank, percentile, C1–C4, quartile_stable, snapshot_id. |
cl_author_metrics | table | Author-level metrics on the same definitions as the journal ones. |
cl_author_selfcit | table | Author self-citation, shared and focal, shown rather than removed. |
cl_org_selfcit | table | Institutional self-citation, direct and through lineage. |
cl_journal_deposit | table | The Crossref deposit scorecard: registration agency, DOI count, reference and ORCID coverage. |
stats_overview | view | One row: journals, publishers, countries, open-access and DOAJ totals. Reads stats_overview_mv, refreshed nightly, so it can trail the tables by up to a day. |
stats_by_country | view | Journal count per country code. |
stats_by_field | view | Journal count per field. |
stats_by_publisher | view | Per publisher: journals, open-access share, maximum and mean h. |
corpus_stats_mv | matview | Corpus population counts. Refreshed nightly, so it can trail the tables by up to a day. |
author_facets_mv | matview | Author counts by country and field, for the directory facets. Refreshed nightly, so it can trail the tables by up to a day. |
author_coverage_mv | matview | How much of the journal–author graph has an author profile behind it. Refreshed weekly, on Sunday, so it can trail the tables by up to seven days. |
metric_facts_mv | matview | Counts describing the metric run: ranked rows, subfields, pools, catch-all articles. Refreshed nightly, so it can trail the tables by up to a day. |
article_language_stats | matview | Article count per language. Refreshed nightly, so it can trail the tables by up to a day. |
catalog_sig_cache | table | One row: the catalogue signature and the time it was computed. What the site compares a cached snapshot against. |
The last five are our view-model, not a data product. The four materialized views and catalog_sig_cache exist to make particular pages of ours fast. Their shape follows our layout and will change when the layout does. Read them if they help; do not build on their columns.
journals.clv is NULL on all 63,664 records, and that is not a bug. No journal has been evaluated. rpc/catalog_sig reports clv: 0 for the same reason. The CiteLink Value is a published rubric with nothing scored against it yet, so a consumer reading that column will find it empty for every row and now knows why.5 · Functions
Called as POST /rest/v1/rpc/{name} with a JSON body of named arguments. Every argument has a default, so {} is a valid body for all of them. These are the 21 that serve data; a further 31 executable names in the same schema belong to the pg_trgm extension and are Postgres, not CiteLink.
| Route | Arguments | Returns |
|---|---|---|
rpc/catalog_sig | — | Catalogue signature: record count and per-index coverage counts. clv is 0. |
rpc/corpus_stats | — | Population counts across articles, journals, authors, organizations. |
rpc/author_facets | — | Author totals with country and field breakdowns and the ORCID count. |
rpc/author_coverage | — | One author_coverage_mv row. |
rpc/metric_facts | — | One metric_facts_mv row. |
rpc/org_facets | — | Organization totals by country and type. The slowest route here; it has been measured at 3.6 s, roughly 45% of the ceiling. |
rpc/cl_subfields | — | Every rankable subfield with its journal count. |
rpc/article_languages | — | Article count per language, descending. |
rpc/cl_quartile_ranking | sfid, lim, off | One subfield’s ranking with intervals and stability flags. |
rpc/lookup_journals | q, lim | Journal typeahead: id, title, ISSN-L, publisher. |
rpc/search_articles | 26 named filters | Article search. Query, author, journal, subfield, type, language, open access, year, citation and percentile ranges, whether the article carries an abstract, sort, page. The abstract filter is the twenty-sixth and was added on 19 September 2026; it narrows a search that already has a query, an author, a subfield, a journal, a DOI or a language behind it, and cannot be a search on its own. |
rpc/search_articles_count | 24 filters, cap | Result count for the same filters, capped — the cap is why a count can come back as 1001. It gained the abstract filter on the same day, because a count that does not take every filter the list takes is not the count of that list. |
rpc/search_authors | 10 named filters | Author search with a capped total. Can still time out on a surname held by tens of thousands of authors — see §3. |
rpc/search_organizations | 9 named filters | Organization search with a total count. |
rpc/get_article | aid | One article joined to its journal and that journal’s quartile row. |
rpc/author_top_articles | aid, lim | An author’s most-cited harvested articles. An empty array is a valid answer. |
rpc/author_journals | aid, lim | The journals an author publishes in, with a per-journal work count. |
rpc/journal_top_authors | jid, lim | A journal’s most prolific authors. |
rpc/tr_fold | t | The search fold for one string. This is the authority the browser’s copy of the fold table is checked against. |
rpc/tr_fold_many | items | The same fold over an array. |
rpc/get_application_status | p_ref, p_email | The status of one indexing application. Returns rows only when the reference and the e-mail on file match, so it cannot be used to enumerate applications. |
A worked call, and its answer:
curl -s https://www.citelink.org/rest/v1/rpc/catalog_sig \
-H "Content-Type: application/json" -d '{}'
{
"n": 58392,
"scopus": 30244,
"wos": 22869,
"doaj": 23227,
"pubmed": 15215,
"pmc": 4190,
"clv": 0
}
Two further functions in the same schema write to a cache table and are not part of this interface. They are not listed because listing a write route on a documentation page is an invitation, and because they should not be reachable with this key at all; withdrawing that is an open item on our side.
Two routes that are not PostgREST — /api/extra/{id} and /api/view/{id}
Everything above this line is PostgREST under /rest/v1/. These two are answered by a small service of our own, on the same origin as the base URL in §2, and it went up on 19 September 2026. It exists because an article page needs two things our own tables do not hold: an author list split into given and family names with ORCID iDs, and the volume, issue and pages an article appeared on. They are given the same three columns as the table above, because they are the same kind of thing — a name you call, what you send it, and what comes back.
| Route | Arguments | Returns |
|---|---|---|
GET /api/extra/{id} |
The article id, in the path — the same id the articles table carries, for example W3025807171. No body, no query string, no key, no header. The id is checked against ^W\d{4,12}$ before anything else happens: anything else is 400, and an id we hold no article for is 404. |
200 and a JSON object: article_id; source, which is crossref, openalex, both or null; fetched_at; authors, each with given, family, orcid (the bare 0000-0000-0000-0000 form) and seq; keywords; biblio with volume, issue, pages, container, container_short, published and type; and views. Every field can be null and every array can be empty — including authors on an article that plainly has authors, and including a name that arrives with a family and no given, or a given and no family. biblio can also arrive as an empty object rather than an object of nulls, so read a key off it defensively. Read the shape as “what the two sources happened to hold”, not as a record that fills. One more case to expect: when several first-time lookups are already in flight the service answers 200 with everything empty rather than queue you behind them, so an empty answer means “not now” at least as often as it means “not known”. Ask again. |
POST /api/view/{id} |
The same id in the path, checked the same way. No body, no key, no header. It writes — the only route on this page that does so unasked. The three intake tables in §6 also take writes, but only when you submit a form; this one is sent by the article page itself, once per article per browser session. | 200 and a two-field JSON object: article_id, and views — the count for that article after adding one. No figure is shown here because the only honest one is whatever the article is on when you call it. It adds one every time it is called; the once-a-session discipline belongs to the article page, not to the route. |
The first route is a cache over Crossref and OpenAlex, and it tells you so in the answer. On a miss our server looks the article up at Crossref by its DOI and at OpenAlex by its work id, stores what comes back and returns it; on a hit nothing leaves the building. source says which of the two the record in front of you came from and fetched_at when it was taken, so a field that looks wrong can be traced to whoever published it and to the day we read it. A stored copy is treated as good for 90 days and fetched again after that, which makes fetched_at readable as an age but still not as an expiry: nothing is deleted at 90 days, the next request for that article simply goes out to the source again. Whether 90 is the right number is an open decision on our side and not a promise to you — if you need to know that a field is current, re-read it rather than trusting the stamp.
views is a count of page opens, and it is the whole of what this site counts. One integer per article, raised by the second route and never by the first — a GET of /api/extra/{id} reads the number and changes nothing, so you can poll it without moving it. Behind it there is no IP address, no cookie, no user agent and no per-visitor record anywhere; the only other thing stored beside the number is the moment that article was last opened by anybody, overwritten each time. The row keys on the article, and there is nothing about a visitor anywhere on this site for it to be joined to. The article page calls the write route once per article per browsing session rather than on every view, so the number counts openings rather than people, and a crawler is counted exactly like a reader: treat it as a floor on attention, not as an audience figure. The article page prints it under the authors. The privacy notice says the same thing from the reader’s side, including what is deliberately not attached to it.
Two things follow for anyone building on it. The author names here are split into given and family, which the articles table’s own author string is not: that string is written by etl/articles-a.sql from the first five authorships only, as display names with no given/family boundary, while n_authors beside it carries the real count — so a row saying 23 authors and listing five is truncation, not disagreement. That string stays the fallback, and it is the one you get when this route answers with an empty authors. And neither route is covered by the commit-time gate described in §1: they are documented, but a reader who finds one unresponsive is telling us something our own build is not.
6 · Endpoints that refuse reads
Three tables accept POST and refuse GET. That is deliberate: the forms on this site work, and nobody — including anyone holding the key above — can read what somebody else submitted.
| Table | Reading it | Why it exists |
|---|---|---|
applications | 401 · 42501 permission denied | Indexing applications from the apply form. |
contact_messages | 401 · 42501 permission denied | Messages from the contact form. |
update_requests | 401 · 42501 permission denied | Correction requests from the publisher panel. |
An applicant reads their own record back through rpc/get_application_status, which requires the reference and the e-mail address together.
7 · Files at the site root
Four static files, no key needed.
| File | What it is |
|---|---|
| errata.xml | Atom feed of the methodology errata. Every defect found in our own metric methodology, newest first, generated from the record itself. See the note below. |
| opensearch.xml | OpenSearch description. Lets a browser search the journal catalogue from its address bar. The home page and the catalogue page carry the <link rel="search"> that offers it; the template lands on journals.html?q=. |
| sitemap.xml | The pages that stand on their own. lastmod comes from a content hash that ignores our cache-buster, so it moves when a page changes and not when a release happens. |
| robots.txt | Open to crawlers, except the query-string surfaces where filter combinations would generate an unbounded number of near-identical URLs. |
About the errata feed. It carries 58 of the 66 entries in the record: eight of the oldest carry no date in their own heading, and rather than date them from the repository’s history — which would assert dates the record itself contradicts — they are left out and the omission is stated in the file. Entries carry a title, a date and a first paragraph, and they are in Turkish, because the record is. Every entry links to the known-defects section rather than to itself; the errata is not rendered as HTML yet, so there is no per-entry page to link to. Each entry is written to stand on its own until there is.
Content types are the host’s business, not ours. Both XML files are served with whatever the static host maps .xml to, which is text/xml by default. Every feed reader accepts that. application/atom+xml and application/opensearchdescription+xml would be more correct and are a one-line host configuration; we do not currently guarantee them. The extension is .xml rather than .atom for exactly this reason — a default host serves an unknown extension as a download.
8 · Which licence covers what
Four different regimes reach you through one endpoint, and the boundary runs down the middle of the tables in §4 — since 6 September 2026 down the middle of a single row of journals, and since 19 September 2026 down the middle of a single column of articles. This paragraph said “three” until that date, when the abstract text stopped being covered by the row it used to sit in. The full text is here.
| Routes | Licence | What that requires of you |
|---|---|---|
articles, authors, organizations, the edge tables, the stats_* views, and the bibliographic columns of journals — not the index-coverage flags, and not clv, best_quartile, best_q_subfield, which are written from the cl_* tables and take the row below; and not articles.abstract, which takes the row directly beneath this one |
CC0 | Nothing. This is bibliographic data from OpenAlex, in the public domain, and we add no restriction on top. |
The abstract column of articles — the abstract text itself, on 63,484,528 of those rows, however you reach it: the table route, rpc/get_article, or the article page that prints it |
CC0 as a record; the text is not ours to license | The abstract comes to us from OpenAlex, whose metadata is CC0, and we add no restriction of our own — that much is the row above. What we cannot do is tell you what you may do with the text. The licence OpenAlex records for a work describes the full text at its open-access location, not the abstract beside it, so a CC BY on the article is not a grant over its abstract. We hold no licence field on an article at all, and where this was sampled against OpenAlex four abstracts in five carried no licence record of any kind. An abstract was written by the authors and published by the journal, and whether your use of one is permitted is their question, not ours to answer for them. Since 19 September 2026 we print these texts on article pages and state where they come from on the methodology page, for the corpus as a whole rather than under every abstract. That is a decision about what this site shows. It is not a licence, and we are not in a position to grant one. |
The index-coverage flags on journals — in_scopus, in_wos, is_in_doaj, in_pubmed, in_pmc, in_ebsco, in_trdizin, in_eric, in_kci, in_erihplus, in_scielo, in_ajol and the fields beside them |
Not ours to license | Each flag says whether one journal appears in somebody else’s index, and that fact came from that operator, not from us. We hold one value per journal and never the list, and we cannot place these in the public domain because they were never ours. Two of the sources attach a condition, and if you redistribute the flags you inherit it — see the table below. |
The cl_* tables and the functions that serve them |
CC BY 4.0 | Attribution must name the edition, not just CiteLink. A quartile from one citation snapshot is not comparable with one from another. The edition is on the rows themselves: snapshot_id and metric_version. For example: “C1 in Immunology, CiteLink edition 2026-07.” |
The methodology prose behind those metrics — including the errata feed — is not covered by either, which is why the feed carries no licence element. Ask if you want to redistribute it.
Where each index flag comes from, and what its source says
Twelve indexes, and they do not arrive under one regime. This table exists because until 7 September 2026 this page said the whole journals table was CC0 from OpenAlex, and that stopped being true as flags from other operators were added to it. Each row names the endpoint we actually read — which is not always the index’s own site — and what that operator publishes about reuse, quoted where it is short enough to quote. Where we could not find a statement, the row says so instead of guessing.
| Flag | What we read | What its publisher says about reuse |
|---|---|---|
is_in_doaj | OpenAlex sources.is_in_doaj — we copy OpenAlex’s field, we do not read doaj.org for it | DOAJ’s terms say its journal-level metadata carries a CC0 waiver and that no attribution is expected. OpenAlex is itself CC0. Nothing required. |
in_ajol | Crossref, member 2895 — the ISSN facet over deposited DOIs. We do not read ajol.info. | Crossref does not use the words “CC0” for member metadata: its documentation calls bibliographic metadata “facts, not subject to copyright” and reserves CC0 for Crossref-generated data. A facet is Crossref-generated. Nothing required. |
in_pubmed, in_pmc, pmc_deposit | The PMC journal list and the NLM catalogue | US Government work, not subject to copyright in the United States. NLM asks rather than requires: “Please acknowledge NLM as the source…” We do, below. |
in_eric | ERIC Currently Cataloged list, March 2026 | IES publishes the ERIC database for public use and states that third parties redownload the metadata onto their own platforms. Its copyright policy governs the full text ERIC hosts, not the bibliography. Nothing required. |
in_scielo | SciELO articlemeta, the network’s own API | The API states no licence. scielo.org carries a site-wide CC BY 4.0 mark, and whether that reaches the API’s metadata is unresolved. We name it rather than treat the programme as silent. |
in_kci, kci_tier | Korea’s open-data portal, dataset 3049043 — 한국연구재단_KCI학술지정보 | The dataset page declares 이용허락범위: 제한 없음 — no restriction. It names no KOGL type and asks for no attribution. We credit the provider below anyway, because a source worth using is worth naming. |
in_cscd, cscd_tier | Chinese Science Citation Database, the list the Chinese Academy of Sciences publishes on its own server — one request, no key | No licence granted and none refused. The page footer reads 版权所有 (all rights reserved) and we could find no positive grant. A letter to CAS is drafted and unsent. We store the tier — core or extended — on the 891 journals already in the catalogue and added no new records, which is narrower than we would take from a source that said yes. |
in_trdizin | TR Dizin (TÜBİTAK ULAKBİM) | No licence granted and none refused. The footer reserves rights; the “Kullanım Şartları” page is a personal-data notice and is silent on reuse. |
in_erihplus, alt_titles | The Zenodo deposit 10.5281/zenodo.8163337 — a snapshot dated 27 April 2023, published to Zenodo on 19 July 2023 by the depositor “ERIH-PLUS”. The flag comes from its rows; alt_titles comes from its Original Title and International Title columns, on 2,473 journals, and exists so that a journal can be found under the name it is actually known by. Nothing is taken from erihplus.hkdir.no itself. | Two statements that do not agree, and we are not going to hide it. The Zenodo record carries CC BY 1.0 Generic. The operator’s own site says: “The content in erih+ is licensed under CC BY 4.0 Non Commercial”. CiteLink is commercial. We are asking HK-dir which governs, and until they answer both fields rest on the Zenodo deposit alone. That deposit is also the only one there, and it is dated 27 April 2023. ERIH PLUS’s own search reported 12,974 journals on 11 September 2026 against the file’s 11,128, so roughly 1,846 journals approved since then are absent from the openly-licensed copy and therefore from us. We have asked HK-dir to deposit a current snapshot; until one exists, this is a gap we can date but not close. |
in_ebsco, ebsco_dbs | EBSCO title lists, per database | EBSCO publishes the lists openly and states no terms for them on that page; its site reserves rights generally. We have not asked, and we do not claim EBSCO published them for this. We store the database codes a journal appears in — on average 6.9 of them — not their contents. |
in_scopus, scopus_source_id | Elsevier’s Scopus Source List — the public workbook, June 2026 edition (ext_list_Jun_2026.xlsx) | We do not have permission, and we have not asked yet. The workbook carries no licence; the site’s terms grant a personal, non-commercial download licence and prohibit “the systematic retrieval of Content… to create or compile, directly or indirectly, a collection, compilation, database or directory” without written permission. A letter is drafted and unsent. That is the position as of 7 September 2026. |
in_wos, wos_editions, wos_categories, wos_abbrev | Clarivate Master Journal List exports, obtained by the index operator through an institutional subscription | Not open. Clarivate’s notice forbids republication or redistribution of its content without written consent, and where an institutional agreement supplied the file that agreement governs too. We have not asked and do not speak for them. Read their terms before reusing this flag. |
What we store, and what the endpoint will actually give you
Until 7 September 2026 this section said “we store whether a journal is in an index, and never the index”. That was a description of a table, offered as if it were a description of the service, and as a description of the service it was false. The correction matters more than the wording, so here it is plainly:
- The flags are filterable on the public route.
GET /journals?select=issn_l&in_scopus=is.trueanswers with the anon key printed in §2, and the response header carries the exact total. Paged at the 1,000-row cap documented in §4, any of these lists comes out in tens of requests. - The bulk snapshot ships them too.
assets/data/catalog.jsonis one file carrying every flag for all 63,664 records, and the site downloads it on a first visit. - Four of the flags are more than one value.
scopus_source_idon 30,239 journals;wos_editions,wos_categoriesandwos_abbrev;ebsco_dbs;kci_tier.
So a reader who wants a reconstruction of somebody’s index can get close to one from this API, and any restriction in the rows above reaches that reader as much as it reaches us. Whether the access itself should change is a separate question and an open one; what is not open is whether this page gets to describe it accurately.
Acknowledgements
Courtesy of the U.S. National Library of Medicine (PubMed, PubMed Central). Journal accreditation data from 한국연구재단 — the National Research Foundation of Korea, Korea Citation Index. ERIH PLUS data from the deposit by ERIH-PLUS, HK-dir. Bibliographic records from OpenAlex. DOI deposit metadata from Crossref. None of these organisations has reviewed, approved or endorsed CiteLink, and nothing here implies that they have.
9 · What is not built
Named here so nobody has to discover it by trying.
| Not available | Why not, plainly |
|---|---|
A REST API at citelink.org/api — still not built; two routes of it now exist | Corrected 19 September 2026. This row read: “The site is static files on a host that cannot execute anything. The design is published on the services page and marked planned. Until there is a server, the routes in §4 and §5 are what exists.” The first sentence stopped being true when the site moved to its own server, and on 19 September 2026 two routes went up under that path: GET /api/extra/{id} and POST /api/view/{id}, both documented at the foot of §5. Two routes are not an API. The design on the services page is still planned rather than built, and those two are the only things under /api/ this page documents — if you find a third answering, §1 says what we owe you: document it or close it. |
| OAI-PMH | Every verb is a dynamic response over query parameters with resumption tokens. This row said “Same reason: no origin to run it”, pointing at the row above; since 19 September 2026 that reason belongs to neither row, because there is an origin and it runs two routes. The true reason is narrower and should stand in its own words: nobody has built it. A static file that answered Identify and nothing else would be worse than saying this. |
| A CSV or JSON dump | 63,664 journal records and 104,664,791 articles belong behind a generator and a schedule. Our plan scopes the dump as a dated, expiring edition; there is no mechanism here to produce or retire one, and a single undated blob committed to a repository is stale the day after it lands. Page §4 instead — 1,000 rows at a time gets you the whole catalogue in 59 requests. |
| OpenSearch suggestions | The extension wants a GET returning a two-element array. rpc/lookup_journals is the natural backend and returns journal rows over POST. Corrected 19 September 2026: this row ended “reshaping that needs a server we do not have”, and that reason expired with the two rows above it — the site runs on its own server and one small service of ours already answers under /api/. The remaining reason is the same one: nobody has written it. |
| A feed of newly indexed journals | No journal has been indexed. Publishing that feed would contradict the footer of the page you are reading. |
| An OpenAPI document — this row is out of date, and kept with its correction rather than removed | Corrected 19 September 2026. This row read: “GET /rest/v1/ is closed to this key by the host’s default. This page is the substitute, and it is checked on every commit, which a schema endpoint would not be.” The first sentence was true while the API lived on Supabase and stopped being true when it moved: §2 of this page has said since 18 September 2026 that GET /rest/v1/ returns PostgREST’s own OpenAPI document. So the document is available, and the row was left contradicting §2 for a day. The second sentence still stands: this page is the one written for people, and it is the one checked on every commit. |
If one of these is the thing standing between you and using this data, say so. The order we build them in should be decided by someone who needs them, not by us guessing.