Vetrix Docs

Site-wide full-text search across code, symbols, issues, merge requests, and documentation, returned as one grouped envelope with a degradation contract for clients that need to fall back when the search backend is unavailable.

Resource overview

Site search is a single endpoint:

GET /api/v1/search

It runs one cross-entity query and returns a grouped envelope with a section per entity type. The query is dispatched to the OpenSearch backend in a single cluster round-trip; when that backend is unavailable the handler degrades to an empty envelope rather than failing (see Degraded results).

Two narrower, deprecated endpoints remain for backward compatibility and have no active client: GET /api/v1/search/code (code-only) and GET /api/v1/search/suggest (type-ahead suggestions). New integrations should call GET /api/v1/search. Per-repository symbol lookup (GET /api/v1/repos/{owner}/{repo}/symbols) and issue search (GET /api/v1/repos/{owner}/{repo}/issues/search) are scoped to a single repository and are documented with their own resources.

Auth & scopes

See conventions.md for the accepted credential types. Site search requires a valid bearer credential — an unauthenticated request is answered 401. There is no dedicated search scope; instead, every result is filtered to what the caller is allowed to read:

  • An instance administrator searches across all repositories.
  • A non-administrator sees hits only from repositories they can read (public, internal, and private repositories where they are a member).

This visibility filter is applied to every section of every query, so search can never surface content from a repository the caller could not otherwise read.

Endpoints

Method Path Summary
GET /api/v1/search Cross-entity site search (grouped envelope)

GET /api/v1/search

Search across the indexed entity types and return a grouped result envelope. Requires authentication; results are visibility-filtered to the caller.

Query parameters

Name Type Default Description
q string Query string. Required; an empty value returns 400.
types string all Comma-separated entity types to query. See Search types. Omitted means the default set. Unrecognized types are ignored.
limit integer 20 Maximum hits per section. Clamped to [1, 100]; a value of 0 or a non-numeric value falls back to 20.
sort string relevance relevance (default) or recent. An unrecognized value is silently treated as relevance — this endpoint does not return 400 for a malformed sort.
Search types

The types parameter selects which sections are populated. The recognized values are:

Type Searches
code Source files across readable repositories.
symbols Code symbols (definitions) across readable repositories.
issues Issues.
mrs Merge requests.
docs Markdown in the configured documentation repositories.

When types is omitted the default set is code, symbols, issues, and mrs. The docs type is opt-in: it is not part of the default set and is populated only when the caller asks for it explicitly (for example types=docs). The docs section is a view over the indexed Markdown of the configured documentation repositories; a deployment with no documentation repositories configured returns an empty docs section.

sort=recent orders the issues and mrs sections by their most recent update. The code, symbols, and docs sections have no date anchor and stay relevance-ranked even when sort=recent is requested.

Response

The envelope always carries all five sections. Each section has a total and a hits array; hits is always an array (never null), even for an unselected or empty section. Each hit carries an id, a relevance score, and a source object whose fields are specific to the entity type.

{
  "code": {
    "total": 1,
    "hits": [
      {
        "id": "alice/widgets:src/main.go",
        "score": 8.42,
        "source": {
          "repo_owner": "alice",
          "repo_name": "widgets",
          "path": "src/main.go",
          "lang": "go"
        }
      }
    ]
  },
  "symbols": { "total": 0, "hits": [] },
  "issues":  { "total": 0, "hits": [] },
  "mrs":     { "total": 0, "hits": [] },
  "docs":    { "total": 0, "hits": [] }
}

A query that matches nothing — or one served while the backend is degraded — returns the same envelope with every section at { "total": 0, "hits": [] }.

Degraded results

Site search depends on the OpenSearch backend. When that backend cannot serve the query — it is not wired on the deployment, the query errored, or the fail-closed safeguard tripped — the handler returns 200 OK with the empty envelope above and sets a response header:

X-Search-Fallback: true

The header signals that the empty result is a degraded response, not a genuine "no results". A healthy backend never sets the header, even when the query legitimately matched zero documents. A client that maintains its own fallback index reads this header to decide whether to fall back. The signal is per-response, not per-section: a degraded types=docs request carries it exactly as a degraded types=code request does.

Status codes

Status When
200 OK Results returned (including a degraded empty envelope — see above).
400 Bad Request q is missing or empty.
401 Unauthorized No valid credential.
429 Too Many Requests The api.expensive budget was exceeded — see Rate limits.
500 Internal Server Error The visibility filter could not be resolved. This is distinct from an OpenSearch outage, which degrades to 200.

Example

curl -H "Authorization: Bearer <token>" \
  "https://api.gitvetrix.com/api/v1/search?q=widget&types=code,issues&limit=10"

Include the documentation section explicitly:

curl -H "Authorization: Bearer <token>" \
  "https://api.gitvetrix.com/api/v1/search?q=deployment&types=docs"

Errors

These endpoints use the shared error envelope and status-code conventions in errors.md. Note the deliberate split above: a missing query is a client error (400) and an unresolvable visibility filter is a server error (500), but an OpenSearch outage is not an error — it degrades to a 200 empty envelope with X-Search-Fallback: true.

Rate limits

Site search is metered under api.expensive rather than api.read, because a single request fans out across multiple indices. The deprecated GET /api/v1/search/code and GET /api/v1/search/suggest endpoints are metered the same way. See rate-limits.md for the per-group budgets and the 429 response.