Skip to main content
Glama

Openalex Get Citation Graph

openalex_get_citation_graph
Read-onlyIdempotent

Walk the citation graph one hop from a seed work. Direction picks the edge: incoming citations (cites), the seed's own references (cited_by), or OpenAlex's algorithmically-related works (related_to). Note: direction follows OpenAlex's filter convention, which inverts the common English reading — cites returns works that cite the seed; cited_by returns works the seed cites. Results use the works schema; combine with filters/sort to narrow further. A page holds per_page works and next_cursor continues it.

Input Schema

NameRequiredDescriptionDefault
sortNoSort field. Prefix with "-" for descending. Comma-separate for a multi-key sort, applied left to right, with the "-" prefix set per key ("-publication_year,cited_by_count" sorts by year descending, then citations ascending). Common: "cited_by_count", "-publication_date". Default is OpenAlex relevance.
cursorNoPagination cursor from a previous response. Pass to get the next page. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for the first page.
selectNoOpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted. `author_list` returns each author in authorship order as `{id, display_name, orcid}` (`orcid` null when absent), a compact alternative to `authorships`, which also carries institutions and affiliations; neither the default nor `["*"]` includes it. A wide projection on a large page makes a large response, so name the fields you need.
filtersNoAdditional filter record object to narrow the graph, using the same value syntax as openalex_search_entities; native string input is unavailable here. Example: publication_year=">2020", is_oa="true". Country fields take ISO 3166-1 alpha-2 codes ("DE") and `language` takes ISO 639-1 codes ("en"); alpha-3, numeric, ISO 639-2, and English-name forms are converted — a multi-word name is one value, so "South Korea|Japan" ORs two countries — and a value that cannot be a country code is rejected. A multi-word value on a field matched as whole text (display_name, awards.funder_display_name, authorships.institutions.geo.city, raw_affiliation_strings) matches as one phrase ("National Science Foundation"; a comma, as in "University of California, Berkeley", is part of it, and it cannot contain a double quote of its own), "!" and "|" included, but cannot be ANDed with "+" in the same field. A value sent unquoted must have paired double quotes; balanced search phrases remain valid. Filters are checked before the seed lookup. A value mixing "|" with an AND operator — a "+", or a space on a field where words mean AND — is rejected, since OpenAlex drops every "|" alternative that ANDs more than one value; put the AND in a second key instead. Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter.
seed_idYesSeed work identifier. Accepts OpenAlex work ID ("W2741809807", or its OpenAlex record-page or API URL such as "https://openalex.org/works/W2741809807"), DOI ("10.1038/nature12373" or full URL), or PMID ("12345678" or "https://pubmed.ncbi.nlm.nih.gov/12345678"). A PMCID is recognized too, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. Use openalex_resolve_name first if you only have a title.
per_pageNoResults per page. Default 25; a larger value returns at most 100 and says so (rejected instead on a server set to OPENALEX_CLAMP_PER_PAGE=false).
directionYes"cites": works that cite seed_id (incoming citations). "cited_by": works that seed_id cites (its reference list). "related_to": OpenAlex algorithmically-related works (~8-30 typical, may be empty for less-cited seeds).

Output Schema

NameRequiredDescriptionDefault
echoNoCompact echo of seed_id, direction, filters, sort — surfaces what was actually queried when no edges are returned.
metaNoResult metadata including pagination.
errorNoPresent when the call failed. Absent on success.
budgetNoWhat this call cost against the OpenAlex daily budget and what is left of it. Price a full walk before committing to it: `totalCount` ÷ `per_page` × `costUsd` against `remainingUsd`. Absent when OpenAlex omitted the accounting headers.
noticeNoGuidance notice. Set when per_page was clamped (requested and applied sizes), when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a cursor continuation says the walk is already past its last edge — and when a work carries exactly 100 authorships or author_list entries (possibly capped). Absent otherwise.
resultsNoWorks on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — except identifier and URL fields such as `id`, `doi`, `ids`, and `*_url`, returned exactly as OpenAlex stores them; an abstract arrives reconstructed as `abstract`, and a selected `author_list` as one `{id, display_name, orcid}` entry per author. Possibly capped author arrays are in `windows`.
windowsNoAuthor arrays possibly capped at 100 on a list record or 5,000 stored on a singleton, and the window of every `slice` call — present even when the slice holds the whole array. A slice window's `next` repeats its `slice.limit` from the following offset; `next` is null at the stored-array end, including a possibly capped singleton.
totalCountNoTotal edges from seed_id in this direction across all pages.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed13 schema fields changed
    • changedInput schema / properties / per_page / description
      Previous value: -"Results per page. Default 25; positive integers above 100 clamp to 100 with a notice unless OPENALEX_CLAMP_PER_PAGE=false restores rejection. A budget-cut page returns fewer (`omitted`)."New value: +"Results per page. Default 25; a larger value returns at most 100 and says so (rejected instead on a server set to OPENALEX_CLAMP_PER_PAGE=false)."
    • removedInput schema / properties / per_page / maximum
      Removed value: -9007199254740991
    • changedInput schema / properties / select / description
      Previous value: -"OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted. `author_list` returns each author in authorship order as `{id, display_name, orcid}` (`orcid` null when absent), a compact alternative to `authorships`, which also carries institutions and affiliations; neither the default nor `[\"*\"]` includes it. Large projections may be cut to fit the response budget (`omitted`, `windows`)."New value: +"OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted. `author_list` returns each author in authorship order as `{id, display_name, orcid}` (`orcid` null when absent), a compact alternative to `authorships`, which also carries institutions and affiliations; neither the default nor `[\"*\"]` includes it. A wide projection on a large page makes a large response, so name the fields you need."
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `unmatched_quote_in_filter_value`: A filter value sent unquoted has an unmatched double quote. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_plan_required`: A filter is one OpenAlex serves only on a paid plan — created_date, updated_date, or a from_/to_ form of either (HTTP 429 \"Plan upgrade required\"). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator, on a field that takes no phrase. Free-text search filters (any `.search` key, such as title.search or abstract.search.exact) and fields matched as whole text (display_name, awards.funder_display_name, awards.funder_award_id, authorships.institutions.geo.city, authorships.institutions.geo.region, raw_affiliation_strings, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase. `invalid_country_code`: A country filter (a key ending in country_code, country_codes, or countries) holds a segment that cannot be a country code, such as a misspelled name, an unassigned number, \"unknown\", or two codes joined by a space (\"DE US\"). `mixed_and_or_in_filter_value`: A filter value puts \"|\" (OR) beside an AND operator — a \"+\", or a space on a field where words mean AND (on a country field a space is always part of a country name, and inside a language name it is part of the name). OpenAlex splits the value on \"|\" first and drops every alternative that ANDs more than one value, so no single value can carry both. `quote_in_filter_value`: A filter value sent to OpenAlex double-quoted holds a double quote inside its outer pair — a value the server quotes (a multi-word value on a field matched as whole text, or a value holding a comma on such a field or on a free-text search filter), or one the caller wrapped in double quotes on any filter but a free-text search. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `id_type_mismatch`: seed_id is a native OpenAlex ID of another entity type — an author, source, institution, topic, publisher, funder, or concept ID rather than a work ID. `id_dot_segment`: seed_id holds a `.` or `..` path segment, written plainly or percent-encoded. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `unmatched_quote_in_filter_value`: A filter value sent unquoted has an unmatched double quote. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_plan_required`: A filter is one OpenAlex serves only on a paid plan — created_date, updated_date, or a from_/to_ form of either (HTTP 429 \"Plan upgrade required\"). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator, on a field that takes no phrase. Free-text search filters (any `.search` key, such as title.search or abstract.search.exact) and fields matched as whole text (display_name, awards.funder_display_name, awards.funder_award_id, authorships.institutions.geo.city, authorships.institutions.geo.region, raw_affiliation_strings, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase. `invalid_country_code`: A country filter (a key ending in country_code, country_codes, or countries) holds a segment that cannot be a country code, such as a misspelled name, an unassigned number, \"unknown\", or two codes joined by a space (\"DE US\"). `mixed_and_or_in_filter_value`: A filter value puts \"|\" (OR) beside an AND operator — a \"+\", or a space on a field where words mean AND (on a country field a space is always part of a country name, and inside a language name it is part of the name). OpenAlex splits the value on \"|\" first and drops every alternative that ANDs more than one value, so no single value can carry both. `quote_in_filter_value`: A filter value sent to OpenAlex double-quoted holds a double quote inside its outer pair — a value the server quotes (a multi-word value on a field matched as whole text, or a value holding a comma on such a field or on a free-text search filter), or one the caller wrapped in double quotes on any filter but a free-text search. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `id_type_mismatch`: seed_id is a native OpenAlex ID of another entity type — an author, source, institution, topic, publisher, funder, award, or concept ID rather than a work ID. `id_dot_segment`: seed_id holds a `.` or `..` path segment, written plainly or percent-encoded. `entity_not_found`: OpenAlex has no work matching the seed_id. `response_too_large`: The response's structuredContent or content[] text would exceed 8,000,000 bytes — a wide `select` on a large page. A page is never shortened to fit. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous value: -[
      -  "unmatched_quote_in_filter_value",
      -  "rate_limited",
      -  "upstream_budget_exhausted",
      -  "upstream_plan_required",
      -  "upstream_timeout",
      -  "upstream_unavailable",
      -  "upstream_unauthorized",
      -  "upstream_forbidden",
      -  "comma_in_filter_value",
      -  "invalid_country_code",
      -  "mixed_and_or_in_filter_value",
      -  "quote_in_filter_value",
      -  "upstream_invalid_params",
      -  "upstream_invalid_id_value",
      -  "upstream_sort_requires_search",
      -  "upstream_invalid_params_other",
      -  "reserved_filter_key",
      -  "id_type_mismatch",
      -  "id_dot_segment",
      -  "entity_not_found"
      -]New value: +[
      +  "unmatched_quote_in_filter_value",
      +  "rate_limited",
      +  "upstream_budget_exhausted",
      +  "upstream_plan_required",
      +  "upstream_timeout",
      +  "upstream_unavailable",
      +  "upstream_unauthorized",
      +  "upstream_forbidden",
      +  "comma_in_filter_value",
      +  "invalid_country_code",
      +  "mixed_and_or_in_filter_value",
      +  "quote_in_filter_value",
      +  "upstream_invalid_params",
      +  "upstream_invalid_id_value",
      +  "upstream_sort_requires_search",
      +  "upstream_invalid_params_other",
      +  "reserved_filter_key",
      +  "id_type_mismatch",
      +  "id_dot_segment",
      +  "entity_not_found",
      +  "response_too_large"
      +]
    • changedOutput schema / properties / meta / properties / next_cursor / description
      Previous value: -"Cursor for next page. null if no more results. Continues after the full upstream page, `omitted` works included."New value: +"Cursor for the next page; pass it back unchanged as `cursor`. null when no further page exists, except on a final page that exactly fills `per_page`: OpenAlex still returns a cursor there, and it leads to an empty page."
    • changedOutput schema / properties / meta / properties / per_page / description
      Previous value: -"Page size OpenAlex echoed for this request — the applied per_page after clamping, not the number of records returned. A short or exhausted page carries fewer records than this, as does a page the response budget cut (`omitted`)."New value: +"Page size OpenAlex echoed for this request — the applied per_page after clamping, not the number of records returned. A short or exhausted page carries fewer records than this."
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance notice. Set when per_page was clamped (requested and applied sizes), when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a cursor continuation says the walk is already past its last edge — when the response budget cut works, windowed an array, or ran over, and when a work carries exactly 100 authorships or author_list entries (possibly capped). Absent otherwise."New value: +"Guidance notice. Set when per_page was clamped (requested and applied sizes), when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a cursor continuation says the walk is already past its last edge — and when a work carries exactly 100 authorships or author_list entries (possibly capped). Absent otherwise."
    • removedOutput schema / properties / omitted
      Removed value: -{
      -  "additionalProperties": false,
      -  "description": "Present when the response budget cut whole records from the page end; pagination continues after the full upstream page.",
      -  "properties": {
      -    "ids": {
      -      "description": "Bare IDs of the records left out, in page order (a keyword keeps its URL).",
      -      "items": {
      -        "type": "string"
      -      },
      -      "type": "array"
      -    },
      -    "next": {
      -      "additionalProperties": false,
      -      "description": "One call returning those records as a set, in OpenAlex's order, not page order. Budgeted too, so it may carry its own `omitted`.",
      -      "properties": {
      -        "arguments": {
      -          "additionalProperties": {},
      -          "description": "Arguments to pass as-is.",
      -          "properties": {},
      -          "type": "object"
      -        },
      -        "tool": {
      -          "const": "openalex_search_entities",
      -          "description": "Tool to call.",
      -          "type": "string"
      -        }
      -      },
      -      "required": [
      -        "tool",
      -        "arguments"
      -      ],
      -      "type": "object"
      -    }
      -  },
      -  "required": [
      -    "ids",
      -    "next"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / properties / over_budget
      Removed value: -{
      -  "description": "True when even the least this call can return — the first record's non-array fields (its arrays windowed to empty), or one `slice` element — exceeds the budget.",
      -  "type": "boolean"
      -}
    • changedOutput schema / properties / results / description
      Previous value: -"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — except identifier and URL fields such as `id`, `doi`, `ids`, and `*_url`, returned exactly as OpenAlex stores them; an abstract arrives reconstructed as `abstract`, and a selected `author_list` as one `{id, display_name, orcid}` entry per author. Works cut by the response budget are in `omitted`; partial or possibly capped arrays are in `windows`."New value: +"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — except identifier and URL fields such as `id`, `doi`, `ids`, and `*_url`, returned exactly as OpenAlex stores them; an abstract arrives reconstructed as `abstract`, and a selected `author_list` as one `{id, display_name, orcid}` entry per author. Possibly capped author arrays are in `windows`."
    • changedOutput schema / properties / windows / description
      Previous value: -"Arrays windowed to fit the budget, paged by `slice`, or possibly capped at 100 list authors or 5,000 stored singleton authors. A `slice` call always carries its window, even for a whole array. `next` is null at the stored-array end, including a possibly capped singleton."New value: +"Author arrays possibly capped at 100 on a list record or 5,000 stored on a singleton, and the window of every `slice` call — present even when the slice holds the whole array. A slice window's `next` repeats its `slice.limit` from the following offset; `next` is null at the stored-array end, including a possibly capped singleton."
    • changedOutput schema / properties / windows / items / description
      Previous value: -"An array returned in part."New value: +"An array returned in part, or possibly in part."
  2. Changed10 schema fields changed
    • changedInput schema / properties / filters / description
      Previous value: -"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Country fields take ISO 3166-1 alpha-2 codes (\"DE\") and `language` takes ISO 639-1 codes (\"en\"); alpha-3, numeric, ISO 639-2, and English-name forms are converted — a multi-word name is one value, so \"South Korea|Japan\" ORs two countries — and a value that cannot be a country code is rejected. A multi-word value on a field matched as whole text (display_name, awards.funder_display_name, authorships.institutions.geo.city, raw_affiliation_strings) matches as one phrase (\"National Science Foundation\"; a comma, as in \"University of California, Berkeley\", is part of it, and it cannot contain a double quote of its own), \"!\" and \"|\" included, but cannot be ANDed with \"+\" in the same field. A value mixing \"|\" with an AND operator — a \"+\", or a space on a field where words mean AND — is rejected, since OpenAlex drops every \"|\" alternative that ANDs more than one value; put the AND in a second key instead. Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter."New value: +"Additional filter record object to narrow the graph, using the same value syntax as openalex_search_entities; native string input is unavailable here. Example: publication_year=\">2020\", is_oa=\"true\". Country fields take ISO 3166-1 alpha-2 codes (\"DE\") and `language` takes ISO 639-1 codes (\"en\"); alpha-3, numeric, ISO 639-2, and English-name forms are converted — a multi-word name is one value, so \"South Korea|Japan\" ORs two countries — and a value that cannot be a country code is rejected. A multi-word value on a field matched as whole text (display_name, awards.funder_display_name, authorships.institutions.geo.city, raw_affiliation_strings) matches as one phrase (\"National Science Foundation\"; a comma, as in \"University of California, Berkeley\", is part of it, and it cannot contain a double quote of its own), \"!\" and \"|\" included, but cannot be ANDed with \"+\" in the same field. A value sent unquoted must have paired double quotes; balanced search phrases remain valid. Filters are checked before the seed lookup. A value mixing \"|\" with an AND operator — a \"+\", or a space on a field where words mean AND — is rejected, since OpenAlex drops every \"|\" alternative that ANDs more than one value; put the AND in a second key instead. Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter."
    • changedInput schema / properties / per_page / description
      Previous value: -"Results per page (1-100). Default 25. A budget-cut page returns fewer (`omitted`)."New value: +"Results per page. Default 25; positive integers above 100 clamp to 100 with a notice unless OPENALEX_CLAMP_PER_PAGE=false restores rejection. A budget-cut page returns fewer (`omitted`)."
    • changedInput schema / properties / per_page / maximum
      Previous value: -100New value: +9007199254740991
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_plan_required`: A filter is one OpenAlex serves only on a paid plan — created_date, updated_date, or a from_/to_ form of either (HTTP 429 \"Plan upgrade required\"). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator, on a field that takes no phrase. Free-text search filters (any `.search` key, such as title.search or abstract.search.exact) and fields matched as whole text (display_name, awards.funder_display_name, awards.funder_award_id, authorships.institutions.geo.city, authorships.institutions.geo.region, raw_affiliation_strings, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase. `invalid_country_code`: A country filter (a key ending in country_code, country_codes, or countries) holds a segment that cannot be a country code, such as a misspelled name, an unassigned number, \"unknown\", or two codes joined by a space (\"DE US\"). `mixed_and_or_in_filter_value`: A filter value puts \"|\" (OR) beside an AND operator — a \"+\", or a space on a field where words mean AND (on a country field a space is always part of a country name, and inside a language name it is part of the name). OpenAlex splits the value on \"|\" first and drops every alternative that ANDs more than one value, so no single value can carry both. `quote_in_filter_value`: A filter value sent to OpenAlex double-quoted holds a double quote inside its outer pair — a value the server quotes (a multi-word value on a field matched as whole text, or a value holding a comma on such a field or on a free-text search filter), or one the caller wrapped in double quotes on any filter but a free-text search. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `id_type_mismatch`: seed_id is a native OpenAlex ID of another entity type — an author, source, institution, topic, publisher, funder, or concept ID rather than a work ID. `id_dot_segment`: seed_id holds a `.` or `..` path segment, written plainly or percent-encoded. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `unmatched_quote_in_filter_value`: A filter value sent unquoted has an unmatched double quote. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_plan_required`: A filter is one OpenAlex serves only on a paid plan — created_date, updated_date, or a from_/to_ form of either (HTTP 429 \"Plan upgrade required\"). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator, on a field that takes no phrase. Free-text search filters (any `.search` key, such as title.search or abstract.search.exact) and fields matched as whole text (display_name, awards.funder_display_name, awards.funder_award_id, authorships.institutions.geo.city, authorships.institutions.geo.region, raw_affiliation_strings, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase. `invalid_country_code`: A country filter (a key ending in country_code, country_codes, or countries) holds a segment that cannot be a country code, such as a misspelled name, an unassigned number, \"unknown\", or two codes joined by a space (\"DE US\"). `mixed_and_or_in_filter_value`: A filter value puts \"|\" (OR) beside an AND operator — a \"+\", or a space on a field where words mean AND (on a country field a space is always part of a country name, and inside a language name it is part of the name). OpenAlex splits the value on \"|\" first and drops every alternative that ANDs more than one value, so no single value can carry both. `quote_in_filter_value`: A filter value sent to OpenAlex double-quoted holds a double quote inside its outer pair — a value the server quotes (a multi-word value on a field matched as whole text, or a value holding a comma on such a field or on a free-text search filter), or one the caller wrapped in double quotes on any filter but a free-text search. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `id_type_mismatch`: seed_id is a native OpenAlex ID of another entity type — an author, source, institution, topic, publisher, funder, or concept ID rather than a work ID. `id_dot_segment`: seed_id holds a `.` or `..` path segment, written plainly or percent-encoded. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous value: -[
      -  "rate_limited",
      -  "upstream_budget_exhausted",
      -  "upstream_plan_required",
      -  "upstream_timeout",
      -  "upstream_unavailable",
      -  "upstream_unauthorized",
      -  "upstream_forbidden",
      -  "comma_in_filter_value",
      -  "invalid_country_code",
      -  "mixed_and_or_in_filter_value",
      -  "quote_in_filter_value",
      -  "upstream_invalid_params",
      -  "upstream_invalid_id_value",
      -  "upstream_sort_requires_search",
      -  "upstream_invalid_params_other",
      -  "reserved_filter_key",
      -  "id_type_mismatch",
      -  "id_dot_segment",
      -  "entity_not_found"
      -]New value: +[
      +  "unmatched_quote_in_filter_value",
      +  "rate_limited",
      +  "upstream_budget_exhausted",
      +  "upstream_plan_required",
      +  "upstream_timeout",
      +  "upstream_unavailable",
      +  "upstream_unauthorized",
      +  "upstream_forbidden",
      +  "comma_in_filter_value",
      +  "invalid_country_code",
      +  "mixed_and_or_in_filter_value",
      +  "quote_in_filter_value",
      +  "upstream_invalid_params",
      +  "upstream_invalid_id_value",
      +  "upstream_sort_requires_search",
      +  "upstream_invalid_params_other",
      +  "reserved_filter_key",
      +  "id_type_mismatch",
      +  "id_dot_segment",
      +  "entity_not_found"
      +]
    • changedOutput schema / properties / meta / properties / per_page / description
      Previous value: -"Page size OpenAlex echoed for this request — the requested per_page, not the number of records returned. A short or exhausted page carries fewer records than this, as does a page the response budget cut (`omitted`)."New value: +"Page size OpenAlex echoed for this request — the applied per_page after clamping, not the number of records returned. A short or exhausted page carries fewer records than this, as does a page the response budget cut (`omitted`)."
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance notice. Set when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead — when the response budget cut works, windowed an array, or ran over, and when a work carries exactly 100 authorships or author_list entries (possibly capped). Absent otherwise."New value: +"Guidance notice. Set when per_page was clamped (requested and applied sizes), when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a cursor continuation says the walk is already past its last edge — when the response budget cut works, windowed an array, or ran over, and when a work carries exactly 100 authorships or author_list entries (possibly capped). Absent otherwise."
    • changedOutput schema / properties / windows / description
      Previous value: -"Arrays returned in part — windowed to fit the budget, paged by `slice`, or a possibly capped 100-entry `authorships` or `author_list` — each with its continuation. A `slice` call always carries its window, even for a whole array; otherwise absent when every array is whole."New value: +"Arrays windowed to fit the budget, paged by `slice`, or possibly capped at 100 list authors or 5,000 stored singleton authors. A `slice` call always carries its window, even for a whole array. `next` is null at the stored-array end, including a possibly capped singleton."
    • changedOutput schema / properties / windows / items / properties / possibly_capped / description
      Previous value: -"True for exactly 100 `authorships` or `author_list` entries on a list record (OpenAlex's cap): the list may be longer."New value: +"True for exactly 100 authorships or author_list entries on a list record, or 5,000 on a singleton lookup: the byline may be longer than the returned array."
    • changedOutput schema / properties / windows / items / properties / total / description
      Previous value: -"Array length as returned to this call."New value: +"Stored array length returned to this call, not a complete byline total."
  3. Changed10 schema fields changed
    • changedInput schema / properties / filters / description
      Previous value: -"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter."New value: +"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Country fields take ISO 3166-1 alpha-2 codes (\"DE\") and `language` takes ISO 639-1 codes (\"en\"); alpha-3, numeric, ISO 639-2, and English-name forms are converted — a multi-word name is one value, so \"South Korea|Japan\" ORs two countries — and a value that cannot be a country code is rejected. A multi-word value on a field matched as whole text (display_name, awards.funder_display_name, authorships.institutions.geo.city, raw_affiliation_strings) matches as one phrase (\"National Science Foundation\"; a comma, as in \"University of California, Berkeley\", is part of it, and it cannot contain a double quote of its own), \"!\" and \"|\" included, but cannot be ANDed with \"+\" in the same field. A value mixing \"|\" with an AND operator — a \"+\", or a space on a field where words mean AND — is rejected, since OpenAlex drops every \"|\" alternative that ANDs more than one value; put the AND in a second key instead. Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter."
    • changedInput schema / properties / seed_id / description
      Previous value: -"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), or PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"). A PMCID is recognized too, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. Use openalex_resolve_name first if you only have a title."New value: +"Seed work identifier. Accepts OpenAlex work ID (\"W2741809807\", or its OpenAlex record-page or API URL such as \"https://openalex.org/works/W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), or PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"). A PMCID is recognized too, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. Use openalex_resolve_name first if you only have a title."
    • changedInput schema / properties / select / description
      Previous value: -"OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted. Large projections may be cut to fit the response budget (`omitted`, `windows`)."New value: +"OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted. `author_list` returns each author in authorship order as `{id, display_name, orcid}` (`orcid` null when absent), a compact alternative to `authorships`, which also carries institutions and affiliations; neither the default nor `[\"*\"]` includes it. Large projections may be cut to fit the response budget (`omitted`, `windows`)."
    • changedOutput schema / properties / budget / properties / costUsd / description
      Previous value: -"USD this call spent, covering both upstream requests — the seed validation lookup (unbilled) and the graph page itself."New value: +"USD this call spent, covering both upstream requests — the seed validation lookup and the graph page itself. The seed lookup is unbilled unless seed_id carries characters past the identifier itself (`W2741809807#x` still resolves to W2741809807), which OpenAlex bills like one list request."
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_plan_required`: A filter is one OpenAlex serves only on a paid plan — created_date, updated_date, or a from_/to_ form of either (HTTP 429 \"Plan upgrade required\"). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator, on a field that takes no phrase. Free-text search filters (any `.search` key, such as title.search or abstract.search.exact) and fields matched as whole text (display_name, awards.funder_display_name, awards.funder_award_id, authorships.institutions.geo.city, authorships.institutions.geo.region, raw_affiliation_strings, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase. `invalid_country_code`: A country filter (a key ending in country_code, country_codes, or countries) holds a segment that cannot be a country code, such as a misspelled name, an unassigned number, \"unknown\", or two codes joined by a space (\"DE US\"). `mixed_and_or_in_filter_value`: A filter value puts \"|\" (OR) beside an AND operator — a \"+\", or a space on a field where words mean AND (on a country field a space is always part of a country name, and inside a language name it is part of the name). OpenAlex splits the value on \"|\" first and drops every alternative that ANDs more than one value, so no single value can carry both. `quote_in_filter_value`: A filter value sent to OpenAlex double-quoted holds a double quote inside its outer pair — a value the server quotes (a multi-word value on a field matched as whole text, or a value holding a comma on such a field or on a free-text search filter), or one the caller wrapped in double quotes on any filter but a free-text search. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `id_type_mismatch`: seed_id is a native OpenAlex ID of another entity type — an author, source, institution, topic, publisher, funder, or concept ID rather than a work ID. `id_dot_segment`: seed_id holds a `.` or `..` path segment, written plainly or percent-encoded. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous value: -[
      -  "rate_limited",
      -  "upstream_budget_exhausted",
      -  "upstream_timeout",
      -  "upstream_unavailable",
      -  "upstream_unauthorized",
      -  "upstream_forbidden",
      -  "comma_in_filter_value",
      -  "upstream_invalid_params",
      -  "upstream_invalid_id_value",
      -  "upstream_sort_requires_search",
      -  "upstream_invalid_params_other",
      -  "reserved_filter_key",
      -  "entity_not_found"
      -]New value: +[
      +  "rate_limited",
      +  "upstream_budget_exhausted",
      +  "upstream_plan_required",
      +  "upstream_timeout",
      +  "upstream_unavailable",
      +  "upstream_unauthorized",
      +  "upstream_forbidden",
      +  "comma_in_filter_value",
      +  "invalid_country_code",
      +  "mixed_and_or_in_filter_value",
      +  "quote_in_filter_value",
      +  "upstream_invalid_params",
      +  "upstream_invalid_id_value",
      +  "upstream_sort_requires_search",
      +  "upstream_invalid_params_other",
      +  "reserved_filter_key",
      +  "id_type_mismatch",
      +  "id_dot_segment",
      +  "entity_not_found"
      +]
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance notice. Set when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead — when the response budget cut works, windowed an array, or ran over, and when a work carries exactly 100 authorships (possibly capped). Absent otherwise."New value: +"Guidance notice. Set when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead — when the response budget cut works, windowed an array, or ran over, and when a work carries exactly 100 authorships or author_list entries (possibly capped). Absent otherwise."
    • changedOutput schema / properties / results / description
      Previous value: -"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — except identifier and URL fields such as `id`, `doi`, `ids`, and `*_url`, returned exactly as OpenAlex stores them. Works cut by the response budget are in `omitted`; partial or possibly capped arrays are in `windows`."New value: +"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — except identifier and URL fields such as `id`, `doi`, `ids`, and `*_url`, returned exactly as OpenAlex stores them; an abstract arrives reconstructed as `abstract`, and a selected `author_list` as one `{id, display_name, orcid}` entry per author. Works cut by the response budget are in `omitted`; partial or possibly capped arrays are in `windows`."
    • changedOutput schema / properties / windows / description
      Previous value: -"Arrays returned in part — windowed to fit the budget, paged by `slice`, or 100 possibly capped authorships — each with its continuation. A `slice` call always carries its window, even for a whole array; otherwise absent when every array is whole."New value: +"Arrays returned in part — windowed to fit the budget, paged by `slice`, or a possibly capped 100-entry `authorships` or `author_list` — each with its continuation. A `slice` call always carries its window, even for a whole array; otherwise absent when every array is whole."
    • changedOutput schema / properties / windows / items / properties / possibly_capped / description
      Previous value: -"True for exactly 100 authorships on a list record (OpenAlex's cap): the list may be longer."New value: +"True for exactly 100 `authorships` or `author_list` entries on a list record (OpenAlex's cap): the list may be longer."
  4. Changed9 schema fields changed
    • changedInput schema / properties / per_page / description
      Previous value: -"Results per page (1-100). Default 25."New value: +"Results per page (1-100). Default 25. A budget-cut page returns fewer (`omitted`)."
    • changedInput schema / properties / select / description
      Previous value: -"OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted."New value: +"OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted. Large projections may be cut to fit the response budget (`omitted`, `windows`)."
    • changedOutput schema / properties / meta / properties / next_cursor / description
      Previous value: -"Cursor for next page. null if no more results."New value: +"Cursor for next page. null if no more results. Continues after the full upstream page, `omitted` works included."
    • changedOutput schema / properties / meta / properties / per_page / description
      Previous value: -"Page size OpenAlex echoed for this request — the requested per_page, not the number of records returned. A short or exhausted page carries fewer records than this."New value: +"Page size OpenAlex echoed for this request — the requested per_page, not the number of records returned. A short or exhausted page carries fewer records than this, as does a page the response budget cut (`omitted`)."
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when no edges are returned. A first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead. Absent when results are present."New value: +"Guidance notice. Set when no edges are returned — a first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead — when the response budget cut works, windowed an array, or ran over, and when a work carries exactly 100 authorships (possibly capped). Absent otherwise."
    • addedOutput schema / properties / omitted
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Present when the response budget cut whole records from the page end; pagination continues after the full upstream page.",
      +  "properties": {
      +    "ids": {
      +      "description": "Bare IDs of the records left out, in page order (a keyword keeps its URL).",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "next": {
      +      "additionalProperties": false,
      +      "description": "One call returning those records as a set, in OpenAlex's order, not page order. Budgeted too, so it may carry its own `omitted`.",
      +      "properties": {
      +        "arguments": {
      +          "additionalProperties": {},
      +          "description": "Arguments to pass as-is.",
      +          "properties": {},
      +          "type": "object"
      +        },
      +        "tool": {
      +          "const": "openalex_search_entities",
      +          "description": "Tool to call.",
      +          "type": "string"
      +        }
      +      },
      +      "required": [
      +        "tool",
      +        "arguments"
      +      ],
      +      "type": "object"
      +    }
      +  },
      +  "required": [
      +    "ids",
      +    "next"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / properties / over_budget
      Added value: +{
      +  "description": "True when even the least this call can return — the first record's non-array fields (its arrays windowed to empty), or one `slice` element — exceeds the budget.",
      +  "type": "boolean"
      +}
    • changedOutput schema / properties / results / description
      Previous value: -"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed."New value: +"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — except identifier and URL fields such as `id`, `doi`, `ids`, and `*_url`, returned exactly as OpenAlex stores them. Works cut by the response budget are in `omitted`; partial or possibly capped arrays are in `windows`."
    • addedOutput schema / properties / windows
      Added value: +{
      +  "description": "Arrays returned in part — windowed to fit the budget, paged by `slice`, or 100 possibly capped authorships — each with its continuation. A `slice` call always carries its window, even for a whole array; otherwise absent when every array is whole.",
      +  "items": {
      +    "additionalProperties": false,
      +    "description": "An array returned in part.",
      +    "properties": {
      +      "field": {
      +        "description": "Array field.",
      +        "type": "string"
      +      },
      +      "id": {
      +        "description": "Bare ID of the record.",
      +        "type": "string"
      +      },
      +      "next": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "arguments": {
      +                "additionalProperties": {},
      +                "description": "Arguments to pass as-is.",
      +                "properties": {},
      +                "type": "object"
      +              },
      +              "tool": {
      +                "const": "openalex_search_entities",
      +                "description": "Tool to call.",
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "tool",
      +              "arguments"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "`id` + `slice` call for the next elements; null at the array end."
      +      },
      +      "offset": {
      +        "description": "Index of the first element shown.",
      +        "type": "number"
      +      },
      +      "possibly_capped": {
      +        "description": "True for exactly 100 authorships on a list record (OpenAlex's cap): the list may be longer.",
      +        "type": "boolean"
      +      },
      +      "shown": {
      +        "description": "Elements shown.",
      +        "type": "number"
      +      },
      +      "total": {
      +        "description": "Array length as returned to this call.",
      +        "type": "number"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "field",
      +      "offset",
      +      "shown",
      +      "total",
      +      "possibly_capped",
      +      "next"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  5. Changed1 schema field changed
    • changedOutput schema / properties / results / description
      Previous value: -"Works on the citation graph in this direction."New value: +"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed."
  6. Changed6 schema fields changed
    • changedInput schema / properties / cursor / description
      Previous value: -"Pagination cursor from a previous response. Pass to get the next page."New value: +"Pagination cursor from a previous response. Pass to get the next page. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for the first page."
    • addedInput schema / properties / cursor / minLength
      Added value: +1
    • changedInput schema / properties / filters / description
      Previous value: -"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Do not include cites/cited_by/related_to — those are set by the `direction` parameter."New value: +"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter."
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to — the direction parameter reserves those keys. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / meta / properties / per_page / description
      Previous value: -"Records on this page."New value: +"Page size OpenAlex echoed for this request — the requested per_page, not the number of records returned. A short or exhausted page carries fewer records than this."
    • changedOutput schema / properties / notice / description
      Previous value: -"Recovery guidance when no edges are returned — suggests verifying the seed_id, broadening filters, or trying a different direction. Absent when results are present."New value: +"Guidance when no edges are returned. A first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead. Absent when results are present."
  7. Changed1 schema field changed
    • changedInput schema / properties / seed_id / description
      Previous value: -"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), or PMCID. Use openalex_resolve_name first if you only have a title."New value: +"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), or PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"). A PMCID is recognized too, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. Use openalex_resolve_name first if you only have a title."
  8. Changed1 schema field changed
    • changedInput schema / properties / seed_id / description
      Previous value: -"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), PMID, or PMCID. Use openalex_resolve_name first if you only have a title."New value: +"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), or PMCID. Use openalex_resolve_name first if you only have a title."
  9. Changed4 schema fields changed
    • removedOutput schema / properties / meta / properties / next_cursor / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / meta / properties / next_cursor / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / results / items / properties / display_name / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / results / items / properties / display_name / type
      Added value: +[
      +  "string",
      +  "null"
      +]
  10. Changed6 schema fields changed
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedInput schema / additionalProperties
      Added value: +false
    • changedOutput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedOutput schema / anyOf
      Added value: +[
      +  {
      +    "not": {
      +      "required": [
      +        "error"
      +      ]
      +    },
      +    "required": [
      +      "meta",
      +      "results",
      +      "echo",
      +      "totalCount"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / error
      Added value: +{
      +  "additionalProperties": {},
      +  "description": "Present when the call failed. Absent on success.",
      +  "properties": {
      +    "code": {
      +      "description": "JSON-RPC error code for this failure.",
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "data": {
      +      "additionalProperties": {},
      +      "properties": {
      +        "reason": {
      +          "description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to — the direction parameter reserves those keys. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler.",
      +          "examples": [
      +            "rate_limited",
      +            "upstream_budget_exhausted",
      +            "upstream_timeout",
      +            "upstream_unavailable",
      +            "upstream_unauthorized",
      +            "upstream_forbidden",
      +            "comma_in_filter_value",
      +            "upstream_invalid_params",
      +            "upstream_invalid_id_value",
      +            "upstream_sort_requires_search",
      +            "upstream_invalid_params_other",
      +            "reserved_filter_key",
      +            "entity_not_found"
      +          ],
      +          "type": "string"
      +        },
      +        "recovery": {
      +          "additionalProperties": {},
      +          "description": "Actionable next step for the caller.",
      +          "properties": {
      +            "hint": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "hint"
      +          ],
      +          "type": "object"
      +        },
      +        "retryable": {
      +          "description": "Whether retrying may succeed.",
      +          "type": "boolean"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "message": {
      +      "description": "Human-readable description of what went wrong.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "code",
      +    "message"
      +  ],
      +  "type": "object"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "meta",
      -  "results",
      -  "echo",
      -  "totalCount"
      -]
  11. Changed1 schema field changed
    • addedOutput schema / properties / budget / properties / prepaidRemainingUsd
      Added value: +{
      +  "description": "USD left in the prepaid balance — a separate pool OpenAlex draws on only after the daily allowance runs out, and which the daily reset does not refill. Add it to `remainingUsd` for the full spendable amount. Absent when the account holds no prepaid balance.",
      +  "type": "number"
      +}
  12. Changed1 schema field changed
    • addedOutput schema / properties / budget
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "What this call cost against the OpenAlex daily budget and what is left of it. Price a full walk before committing to it: `totalCount` ÷ `per_page` × `costUsd` against `remainingUsd`. Absent when OpenAlex omitted the accounting headers.",
      +  "properties": {
      +    "costUsd": {
      +      "description": "USD this call spent, covering both upstream requests — the seed validation lookup (unbilled) and the graph page itself.",
      +      "type": "number"
      +    },
      +    "remainingUsd": {
      +      "description": "USD left in today's OpenAlex budget after this call.",
      +      "type": "number"
      +    },
      +    "resetsInSeconds": {
      +      "description": "Seconds until the daily budget refills (midnight UTC).",
      +      "type": "number"
      +    }
      +  },
      +  "required": [
      +    "costUsd",
      +    "remainingUsd",
      +    "resetsInSeconds"
      +  ],
      +  "type": "object"
      +}
  13. Changed4 schema fields changed
    • changedInput schema / properties / sort / description
      Previous value: -"Sort field. Prefix with \"-\" for descending. Common: \"cited_by_count\", \"-publication_date\". Default is OpenAlex relevance."New value: +"Sort field. Prefix with \"-\" for descending. Comma-separate for a multi-key sort, applied left to right, with the \"-\" prefix set per key (\"-publication_year,cited_by_count\" sorts by year descending, then citations ascending). Common: \"cited_by_count\", \"-publication_date\". Default is OpenAlex relevance."
    • addedOutput schema / properties / results / items / properties / display_name / anyOf
      Added value: +[
      +  {
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedOutput schema / properties / results / items / properties / display_name / description
      Previous value: -"Work title."New value: +"Work title. null when OpenAlex holds no title for the record (paratext works and other untitled entries) — use `id` to identify it."
    • removedOutput schema / properties / results / items / properties / display_name / type
      Removed value: -"string"
  14. Changed3 schema fields changed
    • addedOutput schema / properties / totalCount
      Added value: +{
      +  "description": "Total edges from seed_id in this direction across all pages.",
      +  "type": "number"
      +}
    • removedOutput schema / properties / totalEdges
      Removed value: -{
      -  "description": "Total edges from seed_id in this direction across all pages.",
      -  "type": "number"
      -}
    • changedOutput schema / required
      Previous value: -[
      -  "meta",
      -  "results",
      -  "echo",
      -  "totalEdges"
      -]New value: +[
      +  "meta",
      +  "results",
      +  "echo",
      +  "totalCount"
      +]
  15. Changed1 schema field changed
    • changedOutput schema / properties / echo / description
      Previous value: -"Compact echo of seed_id, direction, filters, sort — useful when no edges are returned so callers see what was actually queried."New value: +"Compact echo of seed_id, direction, filters, sort — surfaces what was actually queried when no edges are returned."
  16. Changed6 schema fields changed
    • addedOutput schema / properties / echo
      Added value: +{
      +  "description": "Compact echo of seed_id, direction, filters, sort — useful when no edges are returned so callers see what was actually queried.",
      +  "type": "string"
      +}
    • removedOutput schema / properties / meta / properties / echo
      Removed value: -{
      -  "description": "Compact echo of seed_id, direction, filters, sort — useful when empty.",
      -  "type": "string"
      -}
    • changedOutput schema / properties / meta / required
      Previous value: -[
      -  "count",
      -  "per_page",
      -  "next_cursor",
      -  "echo"
      -]New value: +[
      +  "count",
      +  "per_page",
      +  "next_cursor"
      +]
    • addedOutput schema / properties / notice
      Added value: +{
      +  "description": "Recovery guidance when no edges are returned — suggests verifying the seed_id, broadening filters, or trying a different direction. Absent when results are present.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / totalEdges
      Added value: +{
      +  "description": "Total edges from seed_id in this direction across all pages.",
      +  "type": "number"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "meta",
      -  "results"
      -]New value: +[
      +  "meta",
      +  "results",
      +  "echo",
      +  "totalEdges"
      +]
  17. Changed1 schema field changed
    • changedInput schema / properties / filters / description
      Previous value: -"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". The direction filter is added automatically — do not pass cites/cited_by/related_to here."New value: +"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Do not include cites/cited_by/related_to — those are set by the `direction` parameter."
  18. Added

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and openWorldHint, so the safety profile is covered. The description adds valuable behavioral context beyond that: the one-hop traversal model, the counterintuitive direction convention, and pagination via per_page/next_cursor.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core operation, then covers direction semantics, the inversion caveat, and pagination in a few tight sentences. Every sentence contributes necessary information without repetition or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the rich schema, annotations, and output schema, the description supplies everything an agent needs to call the tool correctly: seed work, direction semantics, filter/sort narrowing, and pagination behavior. No critical gap remains for this traversal tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents all seven parameters. The description still adds useful meaning, especially the note that 'direction' follows OpenAlex's inverted filter convention and that filters/sort can narrow the graph, which go beyond the schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Walk the citation graph one hop from a seed work.' It then distinguishes the three edge types precisely, so an agent can tell exactly what operation this tool performs versus sibling tools like search_entities or analyze_trends.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly explains how to choose a direction and notes that results can be narrowed with filters/sort. However, it does not explicitly contrast this tool with sibling tools or state when not to use it, leaving some routing inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.