oq

June 22, 2026 · View on GitHub

oq is a pipeline query language for exploring OpenAPI schema reference graphs. It lets you ask structural and semantic questions about schemas and operations at the command line.

Quick Start

# Count all schemas
openapi spec query 'schemas | count' petstore.yaml

# Top 10 deepest component schemas
openapi spec query 'schemas | where(isComponent) | sort-by(depth, desc) | take(10) | select name, depth' petstore.yaml

# Dead components (unreferenced)
openapi spec query 'schemas | where(isComponent) | where(inDegree == 0) | select name' petstore.yaml

Stdin is supported:

cat spec.yaml | openapi spec query 'schemas | count'

Pipeline Syntax

Queries are left-to-right pipelines separated by |:

source | stage | stage | ... | terminal

Sources

SourceDescription
schemasAll schemas (component + inline)
operationsAll operations
componentsAll component types (schemas, parameters, responses, headers, security-schemes). Filter with where(kind == "schema") etc.
webhooksWebhook operations only
serversDocument-level servers
tagsDocument-level tags
securityGlobal security requirements

Traversal Stages

StageDescription
refsBidirectional refs: 1-hop, with direction annotation (/)
refs(*)Bidirectional transitive closure
refs(out) / refs(out, *)Outgoing refs only: 1-hop or closure
refs(in) / refs(in, *)Incoming refs only: 1-hop or closure
refs(N) / refs(out, N)Depth-limited to N hops
properties / properties(*)Property sub-schemas (allOf-flattening). properties(*) recursively expands through $ref, oneOf, anyOf with qualified traversal paths
membersallOf/oneOf/anyOf children, or expand group rows into schemas
itemsArray items schema (with edge annotations)
additional-propertiesExpand to additionalProperties schema
pattern-propertiesExpand to patternProperties schemas
parentNavigate to structural parent schema (via graph in-edges)
to-operationsSchemas → operations
to-schemasOperations → schemas
path(A, B)Shortest bidirectional path between two schemas (with direction annotation)
blast-radiusAncestors + all affected operations

Navigate into the internal structure of operations. These stages produce new row types (parameters, responses, etc.) that can be filtered and inspected.

StageDescription
parametersOperation parameters
responsesOperation responses
request-bodyOperation request body
content-typesContent types from response or request body
headersResponse headers
callbacksOperation callbacks → callback operations
linksResponse links
to-schemaExtract schema from parameter, content-type, or header
operationBack-navigate to source operation
securityOperation security requirements

Analysis Stages

StageDescription
orphansSchemas with no incoming refs and no operation usage
leavesSchemas with no outgoing refs (terminal nodes)
cyclesStrongly connected components (actual cycles)
clustersWeakly connected component grouping
cross-tagSchemas used by operations across multiple tags
shared-refsSchemas shared by ALL operations in result set
duplicatesSchemas sharing the same content hash (at least 2 copies)

Filter & Transform Stages

StageDescription
where(expr)Filter by predicate
select f1, f2Project fields
sort-by(field) / sort-by(field, desc)Sort (ascending by default)
take(N)Limit to first N results
last(N)Limit to last N results
sample(N)Deterministic random sample
highest(N, field)Sort desc + take
lowest(N, field)Sort asc + take
uniqueDeduplicate
group-by(field)Group and count
lengthCount rows
let $var = exprBind expression result to a variable

Meta Stages

StageDescription
explainPrint query plan
fieldsList available fields
format(fmt)Set output format (table/json/markdown/toon/gcf)
to-yamlOutput raw YAML nodes from underlying spec objects

The to-yaml stage uses path (JSON pointer) as the wrapper key for each emitted node, giving full attribution to the source location in the spec.

Function Definitions & Modules

Define reusable functions with def and load them from .oq files with include:

# Inline definitions
def hot: where(inDegree > 10);
def impact($name): where(name == $name) | blast-radius;
schemas | where(isComponent) | hot | select name, inDegree

# Load from file
include "stdlib.oq";
schemas | where(isComponent) | hot | select name, inDegree

Def syntax: def name: body; or def name($p1, $p2): body; Module search paths: current directory, then ~/.config/oq/

Fields

Schema Fields

FieldTypeDescription
namestringComponent name or JSON pointer
typestringSchema type
depthintMax nesting depth
inDegreeintIncoming reference count
outDegreeintOutgoing reference count
unionWidthintUnion member count
propertyCountintProperty count
isComponentboolIn components/schemas
isInlineboolDefined inline
isCircularboolPart of circular reference
hasRefboolHas $ref
hashstringContent hash
locationstringFully qualified JSON pointer
opCountintOperations using this schema
tagCountintDistinct tags across operations

Operation Fields

FieldTypeDescription
namestringoperationId or METHOD /path
methodstringHTTP method
pathstringURL path
operationIdstringoperationId
schemaCountintReachable schema count
componentCountintReachable component count
tagstringFirst tag
parameterCountintParameter count
deprecatedboolDeprecated flag
descriptionstringDescription
summarystringSummary
isWebhookboolWhether the operation is a webhook
callbackNamestringCallback name (set by callbacks stage)
callbackCountintNumber of callbacks

Edge Annotation Fields

Available on rows produced by traversal stages (refs, properties, members, items, path).

FieldTypeDescription
viastringStructural edge kind: property, items, allOf, oneOf, ref, ...
edgestringStructural edge label: property name, array index, pattern, etc.
traversalstringQualified traversal path from seed (e.g. "User/allOf/BaseModel")
schemastringClean immediate parent schema name (last segment of traversal path)
seedstringSeed schema name (the schema that initiated the traversal)
hopsintBFS distance from seed
isRequiredboolWhether the property is in the parent schema's required array
directionstring (outgoing) or (incoming) — set by bidi traversals (refs, path)

Parameter Fields

Produced by the parameters navigation stage.

FieldTypeDescription
namestringParameter name
instringLocation: query, header, path, cookie
requiredboolRequired flag
deprecatedboolDeprecated flag
descriptionstringDescription
stylestringSerialization style
explodeboolExplode flag
hasSchemaboolHas associated schema
allowEmptyValueboolAllow empty value
allowReservedboolAllow reserved characters
operationstringSource operation name

Response Fields

Produced by the responses navigation stage.

FieldTypeDescription
statusCodestringHTTP status code
namestringAlias for statusCode
descriptionstringResponse description
contentTypeCountintNumber of content types
headerCountintNumber of headers
linkCountintNumber of links
hasContentboolHas content types
operationstringSource operation name

Request Body Fields

Produced by the request-body navigation stage.

FieldTypeDescription
namestringAlways "request-body"
descriptionstringRequest body description
requiredboolRequired flag
contentTypeCountintNumber of content types
operationstringSource operation name

Content Type Fields

Produced by the content-types navigation stage.

FieldTypeDescription
mediaTypestringMedia type (e.g. application/json)
namestringAlias for mediaType
hasSchemaboolHas associated schema
hasEncodingboolHas encoding map
hasExampleboolHas example or examples
statusCodestringStatus code (if from a response)
operationstringSource operation name

Header Fields

Produced by the headers navigation stage.

FieldTypeDescription
namestringHeader name
descriptionstringHeader description
requiredboolRequired flag
deprecatedboolDeprecated flag
hasSchemaboolHas associated schema
statusCodestringStatus code of parent response
operationstringSource operation name

Server Fields

FieldTypeDescription
urlstringServer URL
namestringServer name
descriptionstringServer description
variableCountintNumber of server variables

Tag Fields

FieldTypeDescription
namestringTag name
descriptionstringTag description
summarystringTag summary
operationCountintNumber of operations with this tag
FieldTypeDescription
namestringLink name
operationIdstringTarget operation ID
operationRefstringTarget operation reference
descriptionstringLink description
parameterCountintNumber of link parameters
hasRequestBodyboolWhether the link has a request body
hasServerboolWhether the link has a server override
statusCodestringSource response status code
operationstringSource operation

Expressions

oq supports a rich expression language used in where(), let, and if-then-else:

depth > 5
type == "object"
name matches "Error.*"
propertyCount > 3 and not isComponent
has(oneOf) and not has(discriminator)
(depth > 10 or unionWidth > 5) and isComponent
name // "unnamed"                              # alternative: fallback if null/falsy
name default "unnamed"                         # same as above (alias)
if isComponent then depth > 3 else true end   # conditional
"prefix_\(name)"                               # string interpolation

Operators

OperatorDescription
==, !=, >, <, >=, <=Comparison
and, or, notLogical
// (or default)Alternative (returns left if truthy, else right)
has(field)True if field is non-null/non-zero
matches "regex"Regex match
if cond then a else b endConditional (elif supported)
\(expr)String interpolation inside "..."

Variables

Use let to bind values for use in later stages:

schemas | where(name == "Pet") | let $pet = name | refs(out) | where(name != $pet)

Output Formats

Use --format flag or inline format stage:

openapi spec query 'schemas | count' spec.yaml --format json
openapi spec query 'schemas | take(5) | format(markdown)' spec.yaml
FormatDescription
tableAligned columns (default)
jsonJSON array
markdownMarkdown table
toonTOON tabular format
gcfGCF pipe-delimited format with inline schemas

Examples

# Wide union trees
schemas | where(unionWidth > 0) | sort-by(unionWidth, desc) | take(10)

# Central schemas (most referenced)
schemas | where(isComponent) | sort-by(inDegree, desc) | take(10) | select name, inDegree

# Operation sprawl
operations | sort-by(schemaCount, desc) | take(10) | select name, schemaCount

# Circular references
schemas | where(isCircular) | select name, path

# Shortest path between schemas
schemas | path(Pet, Address) | select name

# Walk an operation to connected schemas and back to operations
operations | where(name == "GET /users") | to-schemas | to-operations | select name, method, path

# Explain query plan
schemas | where(isComponent) | where(depth > 5) | sort-by(depth, desc) | explain

# Regex filter
schemas | where(name matches "Error.*") | select name, path

# Group by type
schemas | group-by(type)

# Edge annotations — how does Pet reference other schemas?
schemas | where(isComponent) | where(name == "Pet") | refs(out) | select name, via, edge, traversal

# Blast radius — what breaks if Error changes?
schemas | where(isComponent) | where(name == "Error") | blast-radius | length

# 1-hop bidirectional refs (with direction arrows)
schemas | where(isComponent) | where(name == "Pet") | refs | select name, direction, via, edge

# Orphaned schemas
schemas | where(isComponent) | orphans | select name

# Leaf nodes
schemas | where(isComponent) | leaves | select name, inDegree

# Detect cycles
schemas | cycles

# Discover clusters
schemas | where(isComponent) | clusters

# Cross-tag schemas
schemas | cross-tag | select name, tagCount

# Schemas shared across all operations
operations | shared-refs | select name, opCount

# Variable binding — find Pet's refs(out) schemas (excluding Pet itself)
schemas | where(name == "Pet") | let $pet = name | refs(out) | where(name != $pet) | select name

# User-defined functions
def hot: where(inDegree > 10);
def impact($name): where(name == $name) | blast-radius;
schemas | where(isComponent) | hot | select name, inDegree

# Alternative operator — fallback for missing values
schemas | where(name // "unnamed" != "unnamed") | select name

# --- Navigation examples ---

# List all parameters for a specific operation
operations | where(name == "GET /pets") | parameters | select name, in, required

# Find operations with required query parameters
operations | parameters | where(in == "query" and required) | select name, operation

# Inspect responses for an operation
operations | where(name == "GET /pets") | responses | select statusCode, description

# Drill into content types of a response
operations | where(name == "GET /pets") | responses | where(statusCode == "200") | content-types | select mediaType, hasSchema

# Extract schemas from content types
operations | where(name == "GET /pets") | responses | content-types | to-schema | select name, type

# List response headers
operations | responses | where(statusCode == "200") | headers | select name, required, operation

# Navigate from parameter back to its operation
operations | parameters | where(name == "limit") | operation | select name, method, path

# Request body content types
operations | where(method == "post") | request-body | content-types | select mediaType, hasSchema, operation

# Extract raw YAML for a schema
schemas | where(name == "Pet") | to-yaml

# --- New capabilities ---

# Webhook operations
webhooks | select name, method, path

# Document servers
servers | select url, description, variableCount

# Tags with operation counts
tags | select name, operationCount | sort-by(operationCount, desc)

# Callback operations
operations | where(callbackCount > 0) | callbacks | select name, callbackName

# Response links
operations | responses | links | select name, operationId

# Additional/pattern properties
schemas | where(has(additionalProperties)) | additional-properties
schemas | where(has(patternProperties)) | pattern-properties

# Schemas with default values
schemas | properties | where(has(default)) | select traversal, edge, default

# Extension fields (use underscores for dashes in expressions)
operations | where(has(x_speakeasy_name_override)) | select name, x_speakeasy_name_override

CLI Reference

# Run query-reference for the full language reference
openapi spec query-reference

# Inline query
openapi spec query '<query>' <spec-file>

# Query from file
openapi spec query -f query.oq <spec-file>

# With output format
openapi spec query '<query>' <spec-file> --format json

# From stdin
cat spec.yaml | openapi spec query '<query>'