docs
API

Code search, blast radius and data model

Search indexed repositories, see what a change touches, and read the data model parsed from ORM definitions and SQL.

On for every workspace. The code routes work for every repository your workspace connected through GitHub once its index is ready. Each route is declared under the code_intelligence plan feature. A workspace whose plan lacks it can get 403 INSUFFICIENT_PERMISSIONS with details.feature set to code_intelligence; handle it.

nima keeps its own index of the repositories your workspace connected through GitHub. Three routes read it with an API key or OAuth token, all with scope knowledge:read: one finds files, one says what a change touches, and one returns the tables and columns the code declares. None of them returns source code. Read the code from your own checkout, using the paths and line numbers these routes give you. How the index is built is explained in Code intelligence.

Code search, blast radius and data model routes

The three code intelligence routes, their scope and the MCP tool that calls each one:

MethodPathScopeMCP tool
GET/v1/code-intel/searchknowledge:readsearch_code
GET/v1/code-intel/blast-radiusknowledge:readblast_radius
GET/v1/code-intel/data-modelknowledge:readget_data_model

Paths are relative to https://api.getnima.io. All three are on the soft rate limit tier, 300 requests per 10 seconds. See Rate limits and Scopes and permissions.

Search the codebase

GET/v1/code-intel/search
scopeknowledge:readMCP toolsearch_code

Search the workspace's indexed repositories for files that answer a question. Every repository in the workspace is searched unless you name one. Only repositories whose indexStatus is ready are searched, and every repository comes back in repositories whatever its state, so one response tells you what was searched and what was skipped.

Each repository gets its own 45 second budget, and they run in parallel. A repository that overruns or fails is named in droppedRepositories instead of sinking the call, so a thin result set is not proof that the code is not there.

qstringrequired
What to find, in plain English or as an identifier. Trimmed, then at least 2 characters. Over HTTP there is no maximum; the search_code tool caps it at 300.
repositoryIdstring
Search only this repository. Take the id from repositories in an earlier response. Omit it to search every repository.
limitinteger
File hits to return. Default 8, maximum 20.

limit is clamped rather than rejected: 50 becomes 20, a negative number becomes 1, and 0 or a non-number becomes 8. It applies to each repository and again to the merged list, so you never get more than limit hits in total. There is no pagination and no offset: raise limit to 20, or ask a narrower question.

A search that finds nothing is a 200 with an empty results array, and so is a workspace where no index is ready yet.

Errors from the search route

StatuserrorWhen
400VALIDATION_ERRORq is missing or under 2 characters once trimmed.
401TOKEN_INVALIDThe credential is missing, unknown, revoked or expired.
403INSUFFICIENT_PERMISSIONSThe credential lacks knowledge:read, or the plan gate applies (see the callout at the top), or the token has no workspace (Must belong to an organization).
404RESOURCE_NOT_FOUNDrepositoryId belongs to another workspace (Repository not found), or to nothing (RepositoryCloneEntity not found).
429RATE_LIMITEDOver the soft tier. Retry-After carries the seconds to wait.
500INTERNAL_SERVER_ERRORrepositoryId is not a UUID.

repositoryId is not checked for shape on this route. A value that is not a UUID reaches the database and fails there, so you get a 500 with an incidentId where a 400 belongs. Send an id you read from a repositories array and this never happens. The two 404 texts differ, and the second is an internal name that leaked; match on the status, never on message.

The search response fields

querystring
Your q, trimmed.
repositoriesobject[]
Every repository in the workspace, in any index state. Each has id, repoFullName, indexStatus (pending, indexing, ready or failed; only ready is searched), lastCommitSha (the 40 character commit the index was built from, null before the first index), lastSyncedAt (ISO 8601 UTC or null) and indexWarnings (tokens naming a stage of the last run that did not fully complete; empty when clean).
droppedRepositoriesstring[]
Ids of ready repositories whose search failed or ran past 45 seconds. Their hits are missing from results.
resultsobject[]
File hits, best first, across all searched repositories.

Each entry in results has these fields.

repositoryIdstring
Which repository the file is in.
repoFullNamestring
That repository's owner/name.
filePathstring
Path from the repository root.
scorenumber
A relative ranking score. Not a similarity and not a confidence: compare hits inside one response, nothing else, and do not threshold on it.
summarystring | null
Two or three sentences written by a model at index time. null when the file has none.
matchedChannelsstring[]
Which retrieval channels found the file. See Retrieval channels.
matchenum
exact, semantic or none. Read it as a verdict on the literal you asked for; see below.
symbolsobject[]
Symbols declared in the file, at most 8: name, kind, signature (or null), lineStart and lineEnd (from 1).

match only carries meaning when the query looks like a path (it has a / or ends in a file extension) or a single identifier (one token, optionally with ()). For those, exact means the hit's path equals the path you gave, or the file declares a symbol with that name. semantic means a path or symbol channel matched but the hit is not the literal. none means no channel confirmed the literal: the hit is only a neighbour, and the thing you named may not exist. Any other query, such as a plain-English question, is always semantic.

symbols[].kind is one of function, class, interface, type, enum, route, entity or decorator. symbols is the file's own list, not the symbols that matched, and a file declaring more than eight shows an arbitrary eight.

lastCommitSha is the commit the index was built from. Compare it to the commit you are working on before you trust anything structural: a claim that something does not exist, a line range. The index follows each repository's default branch only and updates when a push lands on it, so work on a feature branch is invisible until it merges.

Get the blast radius of a change

GET/v1/code-intel/blast-radius
scopeknowledge:readMCP toolblast_radius

Given files, symbols or both, return what a change there reaches: the modules that depend on it, the dependent files by distance, the tests that cover it, and the spec documents that govern it. It is read from the existing index and the knowledge graph's code links, with no re-index and no model call, so an index behind your commit answers about the commit it was built from.

Pass a pull request's changed files as paths to review its reach, or a symbol name to answer "who depends on this" before you edit.

pathsstring
Repository-relative file paths, comma-joined (a.ts,b.ts). A shorter suffix of an indexed path also matches. The MCP tool takes an array of at most 50 items, each at most 400 characters, and joins it for you.
symbolstring
An exported symbol to start from, instead of or as well as a path. Comma-separate a few. At most 200 characters in the tool.
repositoryIdstring
Limit to one repository. Omit it to look across every indexed repository.
depthinteger
Import hops to walk outward. Default 2, maximum 4; a larger value is clamped to 4.
modestring
Only agent is accepted, for parity with ask_knowledge_graph. It changes nothing: the response is already compact JSON.

At least one of paths and symbol is required, and together they may name at most 50 targets. Duplicates and blank entries are dropped before counting.

The blast radius response

targetsobject[]
The indexed files the input resolved to: repositoryId, repoFullName, filePath, fileId.
unresolvedstring[]
Inputs no indexed file or symbol matched. A non-empty list with empty targets is a 200, not an error.
depthinteger
The depth actually walked.
dependentModulesobject[]
Modules with files that depend on the targets: moduleId, moduleName, repositoryId, repoFullName, summary, edgeCount, relations, depth.
dependentFilesobject[]
Source files that depend on the targets: repositoryId, repoFullName, filePath, depth (1 means it imports a target directly), relations, moduleName and callers.
dependentFiles[].callersobject[]
The symbols in that file that call into a target: symbol, lineStart, lineEnd, and calls (the target symbol, or null).
coveringTestsobject[]
Test files among the dependents, in the same shape as dependentFiles. A path counts as a test when a directory in it is __tests__, tests or test, or the file name ends in .spec or .test with a JS or TS extension.
governingSpecsobject[]
Spec documents tied to the touched code through the knowledge graph: documentId, title, status, updatedAt, viaEntities.
callGraphobject
coveredTargets, importOnlyTargets and a note. Call edges exist only for .ts, .tsx, .js, .jsx, .mjs and .cjs. For a target in any other language, an empty callers means no parser, not no callers.
truncatedboolean
true when the walk hit its 400 file cap. The answer is then a prefix, not the whole set.

Errors from the blast radius route

StatuserrorWhen
400VALIDATION_ERRORNeither paths nor symbol is given (paths or symbol is required), more than 50 targets (at most 50 paths and symbols per call), or mode is not agent.
401TOKEN_INVALIDThe credential is missing, unknown, revoked or expired.
403INSUFFICIENT_PERMISSIONSMissing knowledge:read, the plan gate, or no workspace on the token.
404RESOURCE_NOT_FOUNDrepositoryId belongs to another workspace or to nothing.
429RATE_LIMITEDOver the soft tier.
500INTERNAL_SERVER_ERRORrepositoryId is not a UUID, as on search.
curl -s -G https://api.getnima.io/v1/code-intel/blast-radius \
  -H "Authorization: Bearer $NIMA_API_KEY" \
  --data-urlencode "paths=src/upload/upload.guard.ts,src/upload/limits.ts" \
  -d "depth=2"

Read the data model

GET/v1/code-intel/data-model
scopeknowledge:readMCP toolget_data_model

Return the tables, collections, columns, relations, indexes and enums that a repository's ORM definitions and SQL declare, each with the file and line it is declared at. The model is parsed at index time with no model call, so it is deterministic for a given commit.

Fourteen ORMs are parsed: Prisma, Drizzle, TypeORM, Sequelize, MikroORM and Mongoose for TypeScript and JavaScript; SQLAlchemy, SQLModel, Django, Peewee and Tortoise for Python; GORM, ent and Bun for Go. SQL CREATE TABLE and ALTER TABLE statements in migrations and schema files are parsed too, and an ORM table shadows a SQL table of the same name. An empty model means no supported ORM or DDL was found, not that the app stores no data.

repositoryIdstring
Only this repository. Must be a UUID. Omit it for every repository in the workspace.
tablestring
A table, collection or model name to return in full, such as users, User or billing.invoices. Case-insensitive, at most 200 characters. Omit it for a summary of every table.

The data model response in summary mode

Without table, the response is { table: null, repositories: [...] } with one entry per repository. Each entry has mode: "summary", the repository fields below, and tables, capped at 400 per repository with truncated set when more exist. A summary table carries name, schema, entityName (the declaring class, null for SQL), orm, kind (table, view, collection or embedded), filePath, lineStart, columnCount and relationTargets.

The data model response in detail mode

With table, each repository entry has mode: "detail" and returns the matching tables in full plus the matching enums. matchedBy is exact when a table's name, schema.name or declaring class equals your query, substring when none did and every table whose name or class contains it is returned, and none when nothing matched. totalMatches is the count before the cap. Detail mode returns at most 20 tables and 50 enums per repository, with truncated set when either cap was hit.

A full table has name, nameSource (explicit or convention), schema, entityName, orm, kind, filePath, lineStart, lineEnd, primaryKey, columns, indexes and relations.

columns[]object[]
name, propertyName, rawType (as written), type (normalized to string, integer, float, decimal, boolean, datetime, date, time, json, uuid, binary, enum, array or unknown), nullable (null when unknowable), primaryKey, unique, defaultValue, enumValues, line.
relations[]object[]
kind (one_to_one, one_to_many, many_to_one or many_to_many), propertyName, targetEntity, targetTable, columns, referencedColumns, throughTable, onDelete, line.
indexes[]object[]
name, columns, unique.

Each detail entry also has enums: name, values, orm, filePath, line, for enums whose name contains your query or that a returned table's enum columns reference.

The repository fields on every data model entry

Every entry, in either mode, starts with repositoryId, repoFullName, commitSha, extractedAt, orms, tableCount, enumCount and warningCount. commitSha is the commit the model was extracted at: compare it to your own before you trust it. extractedAt: null means no model has been extracted for that repository yet, and its counts are zero.

Errors from the data model route

StatuserrorWhen
400VALIDATION_ERRORrepositoryId is not a UUID, or table is over 200 characters.
401TOKEN_INVALIDThe credential is missing, unknown, revoked or expired.
403INSUFFICIENT_PERMISSIONSMissing knowledge:read, the plan gate, or no workspace on the token.
404RESOURCE_NOT_FOUNDrepositoryId belongs to another workspace or to nothing.
429RATE_LIMITEDOver the soft tier.
curl -s -G https://api.getnima.io/v1/code-intel/data-model \
  -H "Authorization: Bearer $NIMA_API_KEY" \
  --data-urlencode "table=invoices"

Retrieval channels

The matchedChannels field of a search hit says why a file surfaced. Your query fans out across several channels at once, the ranks are fused, and a model reranks the candidates against the question. A strong hit usually carries several channels.

ChannelMatched on
keywordFull text search over the file's indexed text.
vectorEmbedding similarity between your query and the file's summary.
vector_hydeThe same embedding index, queried with a hypothetical answer generated from your question.
symbolFull text search over the names of symbols declared in the file.
filepathThe path contains an identifier pulled out of your query.
activityCommit messages and pull request text matched by embedding, mapped back to the files they touched.
graphReached by following import and reference edges out from the top ranked files.

What the code index holds

The code index holds one row per file for 22 file extensions of source code, plus a fixed set of documentation files. Nothing else in a repository is searchable.

IndexedDetail
Code.ts .tsx .js .jsx .mjs .cjs .py .go .java .swift .php .cs .rs .c .h .cpp .cc .cxx .hpp .hh .hxx .sol. These are also the files parsed for symbols.
DocumentationREADME.md, ARCHITECTURE.md, CONTRIBUTING.md, CHANGELOG.md, CLAUDE.md, AGENTS.md, GEMINI.md in any directory except top-level .claude, .github, .husky, .vscode, research, worked, terraform and infrastructure; MANIFEST.md and ROADMAP.md at the root; Markdown files under docs/; and files under .cursor/rules/. These carry no symbols.
Never indexedAnything under node_modules, dist, build, .git, .next, coverage, vendor, __pycache__ or .turbo.

A file type outside that list, such as .rb or .kt, can never appear in results, however well it matches. Summaries are written when the file is indexed, from the first 300 lines of a code file or the first 2,000 lines of a documentation file, so a longer file has a summary describing only its opening, and a file changed since the indexed commit has a summary of the old version. Only the default branch is indexed. Read Code intelligence for how indexing runs.

Code routes not reachable with a key

Five code routes exist for a signed-in browser session and refuse every API key and OAuth token. A route that declares no scope answers 403 with This endpoint does not accept OAuth access tokens, whichever scopes you hold, and the admin panel has no control for any of them.

Method and pathWhat it does
GET /v1/code-intel/repositoriesLists indexed repositories with a file count.
GET /v1/code-intel/repositories/{id}/statsThe last 10 index runs for one repository, with phases, costs and warnings.
GET /v1/code-intel/repos/{repoId}/searchAn older single repository search that returns hits with no index metadata.
GET /v1/code-intel/repos/{repoId}/traces/{conversationId}The retrieval trace of one assistant conversation about that repository.
POST /v1/code-intel/repositories/{id}/reindexTriggers a full re-index, at most once per repository per 24 hours; a second call inside the window answers 429. Needs a signed-in session of a workspace owner or admin.

The repositories array on a search response is the only way to read index state with a key: it carries the id, status, indexed commit, last sync time and warnings for every repository. To refresh an index, push to the default branch and let the index follow, or ask the nima team. There is no route for reading a file's contents.