Skip to content

HTTP API

Reflex includes an HTTP server for programmatic access. Run it from your project directory (the one containing .reflex/):

Terminal window
rfx serve # 127.0.0.1:7878
rfx serve --port 8080 # custom port
rfx serve --host 0.0.0.0 # all interfaces (use with caution)

Search the codebase. Like the CLI, plain queries match whole identifiers by default: handle_as does not match handle_ask unless you pass contains=true.

ParameterTypeRequiredDescription
qstringyesSearch pattern
symbolsboolnoOnly return symbol definitions
kindstringnoFilter by symbol kind (case-insensitive; implies symbols=true)
regexboolnoTreat q as a regular expression
containsboolnoSubstring matching instead of whole identifiers
ignore_caseboolnoCase-insensitive matching
exactboolnoExact identifier match
expandboolnoShow full symbol bodies
langstringnoFilter by language (see Language identifiers)
filestringnoFilter by file path substring
limitintnoMax results (default: 100)
offsetintnoPagination offset
dependenciesboolnoInclude each file’s static imports (Rust only)
forceboolnoBypass broad-query detection
timeoutintnoTimeout in seconds (default: 30)

Response: the same JSON that rfx query --json returns.

{
"status": "fresh",
"can_trust_results": true,
"pagination": {
"total": 1,
"count": 1,
"offset": 0,
"limit": 100,
"has_more": false,
"total_is_exact": true
},
"results": [
{
"path": "src/cli/ask.rs",
"language": "rust",
"matches": [
{
"kind": "Function",
"symbol": "handle_ask",
"span": { "start_line": 9, "end_line": 426 },
"preview": "pub(super) fn handle_ask(\n question: Option<String>,\n auto_execute: bool,\n …"
}
]
}
]
}

kind and symbol appear only on symbol matches. When a search fills its page before verifying every candidate, pagination.total is null, total_is_exact is false, and approx_total holds an estimate:

"pagination": { "total": null, "count": 2, "offset": 0, "limit": 2, "has_more": true, "total_is_exact": false, "approx_total": 1745 }

A whole-identifier search that finds nothing tells you why:

{
"status": "fresh",
"can_trust_results": true,
"pagination": { "total": 0, "count": 0, "offset": 0, "limit": 100, "has_more": false, "total_is_exact": true },
"results": [],
"substring_hint_count": 2,
"hint": "0 whole-identifier matches; 2 substring matches — pass contains:true (--contains on the CLI) to see them. Reflex matches whole identifiers by default, so \"handle_as\" does not match longer names that merely contain it.",
"excluded_reason": "whole_identifier"
}

Status values:

statuscan_trust_resultsMeaning
freshtrueIndex matches the files on disk
stalefalseFiles were edited, added, or deleted since indexing. A warning object lists them (files_modified, files_added, files_deleted)

When can_trust_results is false, trigger a reindex with POST /index. If no index exists, the request fails with an error instead of returning a status.

Examples:

Terminal window
curl "http://localhost:7878/query?q=handle_ask&kind=function"
curl "http://localhost:7878/query?q=HANDLE_ASK&ignore_case=true"
curl "http://localhost:7878/query?q=fn%20handle_&regex=true&lang=rust&limit=20"

Index statistics.

Response:

{
"total_files": 298,
"index_size_bytes": 12389570,
"last_updated": "2026-09-23T18:51:32+00:00",
"files_by_language": {
"Rust": 179,
"Text": 67,
"Python": 18,
"TypeScript": 11,
"Lock": 1
},
"lines_by_language": {
"Rust": 100331,
"Text": 25661,
"Python": 4810,
"TypeScript": 834,
"Lock": 1
},
"corpus_bytes": 4631790,
"trigram_index_bytes": 6224375
}

Returns 404 with {"error":{"kind":"IndexNotFound",...}} if no index exists.


Trigger reindexing. This is synchronous: the response comes back after indexing completes. Indexing is incremental unless you pass force.

Request body (optional; send it with a Content-Type header):

{
"force": false,
"languages": ["rust", "typescript"]
}

Both fields are optional. When force is true, the existing cache is cleared and the index is rebuilt from scratch.

languages limits indexing to the named languages. This endpoint accepts rust/rs, python/py, javascript/js, typescript/ts, vue, svelte, go, java, php, c, and cpp/c++, and ignores other names.

Response: index statistics, in the same schema as GET /stats, plus change counts (new_files, modified_files, unchanged_files, deleted_files) when they’re non-zero.

An unparseable body returns 400:

{"error":{"kind":"ParseError","message":"Invalid JSON body: key must be a string at line 1 column 2"}}

Health check endpoint.

Response:

{"service":"reflex","status":"ok"}

Errors are JSON objects with a kind and a message:

{"error":{"kind":"QuerySyntaxError","message":"Query parameter 'q' cannot be empty"}}
CodeMeaning
200Success
400Invalid request: empty q, unknown lang, query syntax error, or invalid JSON body (QuerySyntaxError, ParseError)
404No index found (IndexNotFound), or unknown endpoint (NotFound)
500Internal error

Use these values for the lang parameter of GET /query (case-insensitive):

LanguageIdentifiers
Rustrust, rs
TypeScripttypescript, ts
JavaScriptjavascript, js
Pythonpython, py
Gogo
Javajava
Cc
C++cpp, c++
C#csharp, cs, c#
Rubyruby, rb
PHPphp
Kotlinkotlin, kt
Vuevue
Sveltesvelte
Zigzig
Plain text (docs, config, templates, extensionless files)text, txt, plaintext, plain
Lock fileslock, lockfile, lockfiles
Generated files (*.pb.go, *.min.js, *.map, *_generated.*)generated, gen

Lock and generated files are indexed but left out of searches unless you select them with lang=lock or lang=generated.

The HTTP server is designed for local use only:

  • Binds to 127.0.0.1 by default
  • No authentication mechanism
  • CORS enabled for all origins (for local tool integration)

If you bind to a non-loopback address such as 0.0.0.0, the server is accessible on your network and prints a warning at startup: WARNING: Reflex server exposed on 0.0.0.0:7878 — NO authentication. Use it only on trusted networks.