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.

Input Schema

TableJSON 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.
filtersNoAdditional 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.
seed_idYesSeed 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.
per_pageNoResults per page (1-100). Default 25.
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

TableJSON 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 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.
resultsNoWorks on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed.
totalCountNoTotal edges from seed_id in this direction across all pages.

Schema Changelog

Changes observed during successful MCP inspections.

  1. 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."
  2. 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."
  3. 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."
  4. 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."
  5. 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"
      +]
  6. 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"
      -]
  7. 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"
      +}
  8. 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"
      +}
  9. 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"
  10. 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"
      +]
  11. 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."
  12. 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"
      +]
  13. 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."
  14. Added

TDQS

A4.8/5.0
Behavior5/5

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

Beyond the readOnlyHint and idempotentHint annotations, the description discloses a critical behavioral quirk: the direction parameter follows OpenAlex's filter convention, so 'cites' returns works that cite the seed and 'cited_by' returns works the seed cites. It also sets expectations that related_to returns roughly 8-30 results and may be empty for less-cited seeds. This is exactly the kind of non-obvious behavior that prevents incorrect invocation.

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?

Three sentences, each earning its place: the first defines scope, the second defines the critical parameter semantics, and the third provides the key inversion warning and result schema context. There is no filler, redundancy, or restating of the title. The most important caveat is front-loaded.

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 annotations, full schema coverage, and presence of an output schema, the description is complete for its complexity. It covers the one-hop traversal, all direction semantics, the counterintuitive naming, result schema, and how to narrow results. Nothing an agent needs to call this tool correctly is missing.

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

Parameters5/5

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

Even though schema coverage is 100%, the description adds conceptual meaning not present in the schema: it explains that direction selects the edge type and warns about the inverted naming convention. It also clarifies that filters/sort operate over the works schema and should not include the direction-related keys, which prevents a common misuse. This meaningfully supplements the already-rich parameter descriptions.

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 opens with a specific verb and resource: 'Walk the citation graph one hop from a seed work.' It clearly distinguishes this tool from siblings like openalex_search_entities by framing it as graph traversal from a seed rather than entity search. The three directional options are explicitly enumerated, making the tool's purpose unmistakable.

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?

The description provides clear context: use this when you need one-hop citation graph traversal from a seed workcache. It advises combining with filters/sort to narrow furtherchers, but it does not explicitly state when to prefer this over sibling tools like openalex_search_entities or openalex_analyze_trends. No alternatives are named, though the purpose is clear enough that an agent can infer appropriate usage.

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.