Skip to content

Use the API ​

Everything the LocalRAG web page does goes through a small HTTP API, so you can use it from scripts and your own apps. The API has no authentication and listens on http://localhost:8090 by default. New to APIs? Start with What is an API? and HTTP basics.

Endpoints ​

MethodPathWhat it does
POST/api/ingestAdds one or more documents.
GET/api/docsLists the documents.
DELETE/api/docs/{doc_id}Deletes one document.
POST/api/queryAsks a question. The answer streams back as Server-Sent Events.
GET/api/healthReports whether the vector store and Ollama are working.
GET/The web page.

Add documents ​

Send the files as a multipart form, with one files field per file. Allowed types are .pdf, .txt and .md.

bash
curl -F "files=@manual.pdf" -F "files=@notes.md" http://localhost:8090/api/ingest

The response lists every file:

json
{
  "documents": [
    {"doc_id": "3f9a1c2b7d10", "filename": "manual.pdf", "pages": 14, "chunks": 38, "deduped": false},
    {"doc_id": "8b02e4f1a9c3", "filename": "notes.md", "pages": 1, "chunks": 2, "deduped": false}
  ]
}
  • doc_id is the first 12 characters of the file's SHA-256 hash. Use it to delete the document.
  • deduped is true when the same contents were already stored. Nothing new is added.

The request is all or nothing. If any file is unsupported or has no text, the server returns 400 with a message that names the file, and adds nothing:

json
{"detail": "scan.pdf: no extractable text in scan.pdf"}

List documents ​

bash
curl http://localhost:8090/api/docs
json
{
  "documents": [
    {
      "doc_id": "3f9a1c2b7d10",
      "filename": "manual.pdf",
      "sha256": "3f9a1c2b7d10…",
      "pages": 14,
      "chunks": 38,
      "ingested_at": "2026-08-10T09:15:02.481193+00:00"
    }
  ]
}

Delete a document ​

bash
curl -X DELETE http://localhost:8090/api/docs/3f9a1c2b7d10

The server answers 204 No Content when the document is deleted, and 404 with unknown document id if there is no such document.

Ask a question ​

Send JSON with a question. top_k, the number of passages to retrieve, is optional; it defaults to 5 and is kept between 1 and 50.

bash
curl -N -X POST http://localhost:8090/api/query \
  -H "Content-Type: application/json" \
  -d '{"question": "What is the warranty period?", "top_k": 5}'

The -N option tells curl to print the answer as it arrives. A blank question returns 400 with question must not be blank.

The event stream ​

The response has the type text/event-stream (Server-Sent Events). It is a series of events, each with an event: line and a data: line of JSON:

text
event: token
data: {"text": "The warranty "}

event: token
data: {"text": "lasts two years."}

event: citations
data: [{"doc": "manual.pdf", "page": 12, "snippet": "Warranty. All pumps are covered for two years...", "score": 0.812}]

event: done
data: {}
EventDataWhen
token{"text": "..."}Zero or more times: one piece of the answer each. Join them in order.
citationsA list of {"doc", "page", "snippet", "score"}Once, after the answer. One entry per page, best score first. snippet is the first 200 characters of the passage.
error{"message": "..."}Instead of citations, if Ollama fails partway. Tokens already sent stay valid.
done{}Always last.

When nothing in your documents clears the score threshold, the stream is one token event with the text I don't know based on the indexed documents., an empty citations list, and done. Ollama is not called.

Read the stream in JavaScript ​

js
const res = await fetch('http://localhost:8090/api/query', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ question: 'What is the warranty period?' })
})

const reader = res.body.getReader()
const decoder = new TextDecoder()
let buffer = ''
let answer = ''

while (true) {
  const { value, done } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  let end
  while ((end = buffer.indexOf('\n\n')) !== -1) {
    const frame = buffer.slice(0, end)
    buffer = buffer.slice(end + 2)
    const event = frame.match(/^event: (.*)$/m)[1]
    const data = JSON.parse(frame.match(/^data: (.*)$/m)[1])
    if (event === 'token') answer += data.text
    if (event === 'citations') console.log('Sources:', data)
    if (event === 'error') console.error(data.message)
  }
}
console.log(answer)

The browser's EventSource cannot send a POST body, so read the stream with fetch as above.

Check health ​

bash
curl http://localhost:8090/api/health
json
{"status": "ok", "chroma": true, "ollama": true, "model": "llama3.2", "docs": 3}
FieldMeaning
statusok when both parts work, otherwise degraded.
chromatrue if the vector store answers.
ollamatrue if Ollama answers at the configured address.
modelThe Ollama model used for answers.
docsThe number of stored documents.

Security ​

The API has no login. With the default pip setup it only listens on 127.0.0.1, your own computer. The Docker setup publishes it on all network connections. Anyone who can reach the port can read from and delete your documents, so do not expose it to networks you do not trust.

Next steps ​