Query language (OQL)

OQO schema

Note: The OpenAlex Query Language is in alpha. It may change without warning — build against it at your own risk, and tell us what you think.

OQO (OpenAlex Query Objects) is the JSON abstract-syntax-tree format that OQL parses into — tools can construct or manipulate queries as OQO directly and skip string parsing entirely. The OQL API accepts both. The JSON Schema:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openalex.org/schemas/oqo/v1.4",
  "name": "OpenAlex_Query_Object",
  "description": "The canonical JSON representation for OpenAlex queries. OQO is the intermediate format for bidirectional translation between URL parameters, OQL (human-readable query language), and programmatic JSON access. GENERATED from query_translation/oqo.py by query_translation/regen_schema.py — do not hand-edit. Entity values are BARE (the namespace is the column_id, resolved via the column registry): 'I136199984', 'de', 'article', '13' — never 'institutions/I136199984'. Negation is the per-node 'is_negated' polarity bit; the canonical form is NNF (negation on leaves only). JSON is the normative serialization; YAML is display-only and MUST round-trip to identical JSON (quote every string value on emit). The OQO describes WHICH ROWS a query matches — nothing else (#661 query/view split, v1.4). View/presentation parameters (sort, column projection, pagination) are NOT part of the OQO; they travel as sibling request parameters on the execute surface, in the classic URL syntax: `sort=<col>[:asc|desc][,…]`, `select=<col>,[…]`, `page`, `per_page`, `cursor` — as query-string params on `GET /?oqo=…` / `GET /?oql=…`, or as sibling top-level body keys on `POST / {\"oqo\": …, \"sort\": …}`.",
  "type": "object",
  "required": [
    "get_rows"
  ],
  "additionalProperties": false,
  "properties": {
    "get_rows": {
      "$ref": "#/$defs/EntityType",
      "description": "The entity type to retrieve (works, authors, institutions, ...)."
    },
    "corpus": {
      "type": "string",
      "enum": [
        "all",
        "core",
        "expansion"
      ],
      "default": "core",
      "description": "Which corpus(es) seed the base result set (works only): 'core' (the curated corpus, default), 'expansion' (the expansion corpus alone — broader coverage, lower quality), or 'all' (core + expansion). A corpus SELECTION, distinct from a filter (which only narrows an already-chosen corpus). Absent ⇒ 'core'."
    },
    "filter_rows": {
      "type": "array",
      "description": "Filters applied to the retrieved rows. Top-level items are implicitly AND-joined.",
      "items": {
        "$ref": "#/$defs/Filter"
      },
      "default": []
    },
    "group_by": {
      "type": "array",
      "description": "Group-by dimensions (Stage B). A LIST, so multi-dimensional grouping (e.g. topic x year) is expressible; dimension order is meaningful. Live serving impl is single-dimension only (multi-dim deferred to #297).",
      "items": {
        "$ref": "#/$defs/GroupBy"
      },
      "default": []
    },
    "sample": {
      "type": [
        "integer",
        "null"
      ],
      "minimum": 1,
      "maximum": 10000,
      "description": "Return a random sample of N results instead of all matches.",
      "default": null
    },
    "seed": {
      "type": [
        "string",
        "integer",
        "null"
      ],
      "description": "Seed that makes a `sample` reproducible. Only meaningful alongside `sample`; ignored (with a warning) otherwise.",
      "default": null
    }
  },
  "$defs": {
    "EntityType": {
      "type": "string",
      "description": "Valid OpenAlex entity types (the OQO `get_rows`). Sourced from oqo.VALID_ENTITY_TYPES.",
      "enum": [
        "authors",
        "awards",
        "concepts",
        "continents",
        "countries",
        "domains",
        "fields",
        "funders",
        "institution-types",
        "institutions",
        "keywords",
        "languages",
        "licenses",
        "locations",
        "oa-statuses",
        "publishers",
        "sdgs",
        "source-types",
        "sources",
        "subfields",
        "topics",
        "types",
        "works"
      ]
    },
    "Filter": {
      "description": "A leaf filter (single condition) or a branch filter (boolean combination).",
      "oneOf": [
        {
          "$ref": "#/$defs/LeafFilter"
        },
        {
          "$ref": "#/$defs/BranchFilter"
        }
      ]
    },
    "LeafFilter": {
      "type": "object",
      "description": "A single filter condition (a literal = atom + polarity).",
      "required": [
        "column_id",
        "value"
      ],
      "additionalProperties": false,
      "properties": {
        "column_id": {
          "type": "string",
          "description": "Filter field identifier; dot notation for nested fields. Valid columns/types come from the column registry, not this schema.",
          "examples": [
            "publication_year",
            "type",
            "open_access.is_oa",
            "authorships.institutions.lineage",
            "title_and_abstract.search",
            "sustainable_development_goals.id"
          ]
        },
        "value": {
          "description": "BARE value (no entity prefix). Native IDs self-namespace via their letter prefix (A/W/I/S/F/T/...); non-native values are bare slugs/codes/ints disambiguated by column_id. String for ids/slugs/search, integer for counts/years, boolean for flags, null for missing.",
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "integer"
            },
            {
              "type": "number"
            },
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "examples": [
            "article",
            "I136199984",
            "de",
            "13",
            2024,
            true,
            null
          ]
        },
        "operator": {
          "$ref": "#/$defs/Operator",
          "description": "Comparison operator. Defaults to 'is'. Strictly affirmative — negation is `is_negated`, never an operator.",
          "default": "is"
        },
        "is_negated": {
          "type": "boolean",
          "description": "Polarity bit. true = the negation of this leaf (the single negation mechanism; maps 1:1 to URL `!`). In canonical (NNF) form, negation lives only on leaves.",
          "default": false
        }
      }
    },
    "BranchFilter": {
      "type": "object",
      "description": "A boolean combination of filters (AND/OR), optionally negated.",
      "required": [
        "join",
        "filters"
      ],
      "additionalProperties": false,
      "properties": {
        "join": {
          "type": "string",
          "enum": [
            "and",
            "or"
          ],
          "description": "'and' requires all children; 'or' requires at least one."
        },
        "filters": {
          "type": "array",
          "description": "Child filters (leaf or branch), enabling nested boolean logic.",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/Filter"
          }
        },
        "is_negated": {
          "type": "boolean",
          "description": "Negate the whole branch. The canonicalizer pushes this to the leaves via De Morgan (NNF), so a canonical OQO has is_negated only on leaves.",
          "default": false
        }
      }
    },
    "GroupBy": {
      "type": "object",
      "description": "A single group-by dimension.",
      "required": [
        "column_id"
      ],
      "additionalProperties": false,
      "properties": {
        "column_id": {
          "type": "string",
          "description": "The column to group by.",
          "examples": [
            "primary_topic.id",
            "publication_year",
            "authorships.countries",
            "sustainable_development_goals.id"
          ]
        }
      }
    },
    "Operator": {
      "type": "string",
      "description": "Leaf comparison operators (strictly affirmative). Sourced from oqo.VALID_OPERATORS. Negation is the `is_negated` bit, not an operator.",
      "enum": [
        "<",
        "<=",
        ">",
        ">=",
        "has",
        "in collection",
        "is"
      ],
      "default": "is"
    }
  },
  "examples": [
    {
      "description": "Type filter (bare value)",
      "value": {
        "get_rows": "works",
        "filter_rows": [
          {
            "column_id": "type",
            "value": "article"
          }
        ]
      }
    },
    {
      "description": "AND of filters (implicit at top level), with range + boolean flag",
      "value": {
        "get_rows": "works",
        "filter_rows": [
          {
            "column_id": "type",
            "value": "article"
          },
          {
            "column_id": "publication_year",
            "value": 2024,
            "operator": ">="
          },
          {
            "column_id": "open_access.is_oa",
            "value": true
          }
        ]
      }
    },
    {
      "description": "OR within a field (article OR review)",
      "value": {
        "get_rows": "works",
        "filter_rows": [
          {
            "join": "or",
            "filters": [
              {
                "column_id": "type",
                "value": "article"
              },
              {
                "column_id": "type",
                "value": "review"
              }
            ]
          }
        ]
      }
    },
    {
      "description": "Negation via is_negated (COVID NOT pediatric)",
      "value": {
        "get_rows": "works",
        "filter_rows": [
          {
            "column_id": "title_and_abstract.search",
            "value": "covid",
            "operator": "has"
          },
          {
            "column_id": "title_and_abstract.search",
            "value": "pediatric",
            "operator": "has",
            "is_negated": true
          }
        ]
      }
    },
    {
      "description": "Stage B: multi-dimensional group_by (topic x year)",
      "value": {
        "get_rows": "works",
        "filter_rows": [
          {
            "column_id": "publication_year",
            "value": 1976,
            "operator": ">="
          }
        ],
        "group_by": [
          {
            "column_id": "primary_topic.id"
          },
          {
            "column_id": "publication_year"
          }
        ]
      }
    },
    {
      "description": "Reproducible random sample (seed makes the sample stable)",
      "value": {
        "get_rows": "works",
        "filter_rows": [
          {
            "column_id": "publication_year",
            "value": 2024,
            "operator": ">="
          }
        ],
        "sample": 100,
        "seed": "42"
      }
    }
  ]
}
View as Markdown