<!-- generated by build_articles.py — edit content/answers/search-openapi-spec-for-agent.md instead -->
---
title: "Let an agent look up endpoints in an API spec"
canonical: "https://xerj.org/answers/search-openapi-spec-for-agent"
updated: "2026-08-21"
source: "content/answers/search-openapi-spec-for-agent.md"
---

# My agent needs to look up endpoints in a big OpenAPI spec and the SDK markdown. What's the right way?

**TL;DR** — XERJ makes an OpenAPI document queryable when `xerj autoindex` reads the file from disk. In a captured run, a route name, a description and an `operationId` each returned 2 hits, 1 per serialization. Extraction is format-based: the whole spec produced only `body` and `title`.

## Agent prompt

```text
Act as a coding agent. Read https://xerj.org/llms.txt, start a local XERJ node, put an OpenAPI document on disk as both .json and .yaml, run `xerj autoindex ./api-specs --url http://127.0.0.1:9200 --prefix oa --progress plain`, then POST a match on body for a route name and for an operationId, GET /oa-docs/_mapping, and report the ax_format of every hit plus the complete field list the spec produced.
```

## Commands

### Command 1

Note: Index a folder of OpenAPI documents from local disk.

```sh
xerj autoindex ./api-specs --url http://127.0.0.1:9200 --prefix oa --progress plain
```

### Command 2

Note: Find a route name across every serialization of the spec.

```sh
curl -s -XPOST http://127.0.0.1:9200/oa-*/_search -H 'content-type: application/json' -d '{"query":{"match":{"body":"quokka-orders"}},"size":10,"_source":["ax_path","ax_format","ax_locator"],"track_total_hits":true}'
```

### Command 3

Note: Find an operation description written in the spec summary.

```sh
curl -s -XPOST http://127.0.0.1:9200/oa-*/_search -H 'content-type: application/json' -d '{"query":{"match_phrase":{"body":"Retrieve quokka orders by region"}},"size":10,"_source":["ax_path","ax_format","ax_locator"],"track_total_hits":true}'
```

### Command 4

Note: Read the complete field list, which shows that extraction is format-based.

```sh
curl -s -XGET http://127.0.0.1:9200/oa-docs/_mapping
```

## Index the spec file itself

XERJ treats an OpenAPI document as structured data in its own format. A `.json` spec goes through the JSON family and a `.yaml` spec through the YAML family, and both land in the same index.

```sh
xerj autoindex ./api-specs --url http://127.0.0.1:9200 --prefix oa --progress plain
```

The captured run read 2 files into 1 dataset and 4 documents live in `oa-docs`, with 0 junk files. The 2 files were 1 valid OpenAPI 3.0.3 document, serialized once as JSON and once as YAML by the fixture generator.

## Both serializations answer the same query

Each query below ran once against `/oa-*/_search` and returned 1 hit per serialization. The `ax_format` field on the hit says which file matched.

| query | hits | formats returned |
| --- | --- | --- |
| `match` on `body` for `quokka-orders` | 2 | `json` and `yaml` |
| `match_phrase` on `body` for `Retrieve quokka orders by region` | 2 | `json` and `yaml` |
| `match` on `body` for `getOrderCheckpointJournal` | 2 | `json` and `yaml` |

```sh
curl -s -XPOST 'http://127.0.0.1:9200/oa-*/_search' \
  -H 'content-type: application/json' \
  -d '{"query":{"match":{"body":"quokka-orders"}},"size":10,"_source":["ax_path","ax_format","ax_locator"],"track_total_hits":true}'
```

Keep both serializations on disk only if you want both. Otherwise a single-file query returns 1 hit for the same text.

## Extraction is format-based, not spec-aware

The whole document produced 2 content fields: `body` and `title`. The rest of the mapping is XERJ provenance: `ax_dataset`, `ax_file`, `ax_format`, `ax_locator`, `ax_path`, `ax_paths` and `ax_run`.

There is no `paths` field, no `operationId` field and no per-operation document. A route name matches because the string is in the text, not because XERJ modelled the route.

Plan around that. Use XERJ to find the document and the position, then let your agent parse the spec for parameter types, request bodies and response schemas.

## Read the hits before you trust a bucket count

If you aggregate over spec documents, compare the buckets with the returned hits for that exact query. A terms aggregation is not reliably scoped to the full-text query it travels with.

One captured matrix on a single index makes the risk concrete.

| query type | documents matched | aggregation buckets |
| --- | --- | --- |
| `match_all` | 674 | 331 |
| `exists` | 315 | 315 |
| `term` | 630 | 315 |
| `match_phrase` | 11 | 0 |
| `match` | 11 | 74 |

This is not a universal rule, and the same second pass recorded an aggregation that agreed exactly with its hits. Publish the number you read from the hits.

## What this capture does not show

The spec in this run came from the fixture generator, not from a vendor or a live API. No API server ran, and XERJ fetched nothing over the network. XERJ has no `$ref` resolver and no schema validator.

XERJ runs single-node here, with no replication and no failover. The default embedder in XERJ is lexical feature hashing, so a query and a paraphrase that share no words do not match. Neural embeddings are opt-in through `--embed-mode neural`.

Every number above comes from RUN-B and RUN-G, captured on 2026-08-21. The binary was a `ci-test` profile build, so no wall-clock figure from these runs is published as a performance number.

## FAQ

### My agent needs to look up endpoints in a big OpenAPI spec. What's the right way?

Put the spec on disk, run `xerj autoindex` on its folder, and have the agent send a `match` on `body`. The captured run made route names, descriptions and operationIds queryable with 1 command.

### How do I make an OpenAPI spec searchable for my coding agent?

Index the folder that holds it and expose the index to the agent. Extraction is format-based, so the agent queries the document text rather than a typed route object.

### How do I search a spec and the docs folder together?

Keep them in one folder and index it once. The spec and the SDK markdown become documents in the same run, so one index pattern reaches both and each hit carries `ax_path` and `ax_format`.

### Does XERJ parse OpenAPI as a specification?

No. The whole document produced only `body` and `title` beyond the provenance fields. Extraction is format-based, so a path is text and not a typed route object.

### Can I search for an operationId?

Yes. A `match` on `body` for `getOrderCheckpointJournal` returned 2 hits, 1 per serialization. The operationId is ordinary text inside the document.

### Do I need a separate parser for path parameters?

Yes, if you want typed routes. XERJ returns the matching document and its `ax_path`, and your agent parses the spec itself for parameter structure.

### Can I count routes with an aggregation?

Read the hits, not only the buckets. A captured matrix shows `match_phrase` matching 11 documents while its terms aggregation returned 0 buckets.

## Related

- [How do I find every place a config key is set across YAML and XML in a repo?](/answers/search-yaml-xml-config-repository)
- [I need my agent to find a passage and cite the file, not dump the whole tree. How?](/answers/code-search-mcp-for-claude-code)
- [How should an agent figure out what's in a messy data folder before searching?](/answers/catalog-files-with-autoindex-map)
