---
title: "Lookeen RAG API reference"
url: https://lookeen.com/solutions/rag/api
lang: en
description: "Search documents, retrieve relevant passages and read document text through the Lookeen RAG API."
published: 2026-10-23T10:00:00+00:00
modified: 2026-11-11T10:00:00+00:00
---

# Lookeen RAG API

Search your documents, find relevant passages and read the source text. Your application handles the answer.

## Give your application evidence, not just an answer

The Lookeen RAG API connects your applications and agents to indexed email, files and Microsoft 365 content through one retrieval interface. Instead of sending entire archives to a language model, find relevant documents and passages first, then read more only where needed.

Lookeen retrieves the evidence; your application decides what to do with it. Build an internal assistant, add supporting documents to a business workflow or let an agent check sources before taking its next step. You choose the language model, the context it receives and how answers cite their sources.

## From a question to a sourced answer

The usual flow: find evidence first, then read it, then answer with sources.

1.  **Search for evidence.** Send a query to `POST /v1/rag`. Use keyword search for names, exact phrases and known terms, or `mode: "semantic"` for questions and concepts that may use different wording in the source. Filters narrow either mode by folder, date, document type and other metadata. The optional `user` applies a user's document permissions.
2.  **Inspect the results.** Keyword results include short excerpts; semantic results are ranked passages, each carrying its document metadata and page number when available. Use them to choose which sources deserve a closer look.
3.  **Read or download only what you need.** Pass a result's `id` to `GET /v1/rag/documents/{id}` for extracted text, or `GET /v1/rag/documents/{id}/content` for the original file bytes. Request a specific page or Excel sheet when the passage needs surrounding context.
4.  **Answer with sources.** Give the selected text to your model or agent and retain the document references and available page numbers for citations. Lookeen does not generate the answer. If the retrieved material does not support a conclusion, refine the search or make that gap explicit rather than inventing one.

For example, an agent investigating why a customer migration was delayed can search the question semantically, inspect passages from status reports and emails, then read the relevant page before answering with a source reference. It does not need to load every project document into its context.

## Explore before you search

An application or agent does not have to guess how the indexed content is organized. The lookup endpoints below list sources, browse folders and discover tags. Use a returned folder reference or tag name to scope the next search. The users endpoint helps resolve a UPN or email address when a user-specific permission context is needed; `user` remains optional.

## Search

```bash
curl https://lookeen.example/v1/rag \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Content-Type: application/json' \
  -d '{"query": "customer migration"}'
```

`POST /v1/rag` returns matching documents. Keyword search and relevance ordering are the defaults.

### Request

| Field | Type | Description |
| --- | --- | --- |
| `query` | String | Keyword query or natural-language question. Required and non-empty for semantic search. Optional for keyword searches using filters. |
| `user` | String | Optional user principal name (UPN) or email address. Lookeen looks up the user and applies their permissions. Omit to search without a user-specific permission filter. |
| `mode` | String | `keyword` (default) or `semantic`. Semantic search returns an error if it is not enabled. |
| `limit` | Integer | Maximum results to return: 1–50, default 10. Counts documents in keyword mode and passages in semantic mode. |
| `offset` | Integer | Number of matching results to skip: documents in keyword mode, passages in semantic mode. Default 0. |
| `sort` | String | `relevance` (default), `modified_desc` (newest first) or `modified_asc` (oldest first). |
| `filters` | Object | Optional document filters. See below. |

Use `offset` with `limit` to retrieve subsequent results. For example, `"offset": 10, "limit": 10` returns the next ten results. Both modes support pagination and all three sort options. In semantic mode each passage is a separate result; a document can therefore occur more than once. The searchable result window is limited to the first 1,000 results (`offset + limit` must not exceed 1,000).

Responses do not include a total count or continuation token. Stop when fewer than `limit` results are returned; if a page is full, request the next page. Results are not a snapshot: indexing changes between requests can cause skipped or repeated results.

### Query syntax

In keyword mode, separate words match with **AND** by default. Use uppercase `OR` for alternatives, `AND` to require both terms, and `NOT` to exclude a term. Group expressions with parentheses rather than relying on operator precedence. Single or double quotes match a phrase; a backslash escapes a query syntax character.

```json
{"query": "(invoice OR bill) AND 'customer migration' NOT draft"}
```

In semantic mode, `query` is natural-language text, not a Boolean expression: `OR`, quotes and parentheses do not apply keyword operators. Use `filters` for metadata restrictions in either mode. Filter values follow the matching rules below, not keyword query syntax.

### Filters

Use a value or wildcard pattern for each field. Use an array to match any of several values, and a range object for dates or sizes.

```json
{
  "query": "invoice OR bill",
  "filters": {
    "name": ["*invoice*", "*bill*"],
    "source": ["file", "m365"],
    "extension": ["pdf", "docx"],
    "modified": {
      "from": "2026-01-01",
      "to": "2026-12-10"
    }
  }
}
```

| Field | Meaning / values | Example |
| --- | --- | --- |
| `name` | File name or document title; email subject for messages. | `"invoic*"` |
| `path` | Full document path, including its name. | `"*Accounting*invoice*"` |
| `folder` | Folder pattern or a URL returned by the folders endpoint. A folder URL includes its subfolders. | `"*Accounting*"` |
| `source` | `FILE`, `EXCHANGE`, `O365`, `PST`, `M365` (Microsoft Graph), `IMAP`. | `["file", "m365"]` |
| `itemType` | `file`, `mail`, `attachment`, `note`, `appointment`, `person`, `meeting`, `task`. | `"mail"` |
| `extension` | File extension. | `["pdf", "docx"]` |
| `size` | Document size in bytes, as a range. | `{"to": 1048576}` |
| `created` | Source creation date/time, as a range. | `{"from": "2026-01-01"}` |
| `modified` | Source modification date/time, as a range. | `{"to": "2026-12-10"}` |
| `from` | Email sender address or display name. | `"*@microsoft.com"` |
| `to` | Any To recipient address or display name. | `"accounts@company.com"` |
| `cc` | Any Cc recipient address or display name. | `"*@company.com"` |
| `attachment` | Attachment filename. Matches the email containing the attachment. | `["invoice*", "bill*"]` |
| `tags` | Lookeen tag names. | `"Accounting*"` |

#### Matching values

-   Different fields combine with **AND**. Arrays always mean **OR**: `"name": ["*invoice*", "*bill*"]` matches either pattern.
-   Text supports `*` for any number of characters and `?` for one character. Without wildcards, the complete value must match. This applies to names, paths, folders, email addresses/display names, attachment filenames and tags.
-   Comparisons ignore case and surrounding whitespace. Source and item type accept the values listed above in any case. Extensions accept either `.PDF` or `pdf`.
-   For recipients, attachments and tags, matching any entry is sufficient.

#### Ranges

`created`, `modified` and `size` use `from` and `to`. Both boundaries are inclusive; omit either boundary for an open-ended range.

```json
{
  "filters": {
    "modified": {"from": "2026-01-01", "to": "2026-12-10"},
    "size": {"to": 1048576}
  }
}
```

Dates accept `YYYY-MM-DD` or ISO-8601 timestamps. Date-only boundaries include the whole day in UTC; timestamps specify the exact instant.

#### Example: invoice emails from Microsoft

```json
{
  "query": "invoice OR bill",
  "filters": {
    "itemType": "mail",
    "from": "*@microsoft.com",
    "attachment": ["invoice*", "bill*"],
    "tags": ["Accounting", "NeedsReview"]
  }
}
```

This finds emails from Microsoft with an attachment starting with “invoice” or “bill”, and either of the Lookeen tags “Accounting” or “NeedsReview”.

### Keyword results

```json
{
  "documents": [
    {
      "id": "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU~3~42",
      "name": "Migration status.pdf",
      "source": "M365",
      "itemType": "file",
      "modified": "2026-10-14T08:30:00Z",
      "body": "The migration was postponed pending security approval."
    }
  ],
  "elapsedMs": 12
}
```

`id` identifies the document for reading and citations. It remains stable across reindexing and content updates, but changes when the document moves because it encodes the source path. Treat it as an opaque reference; do not construct or parse IDs. An ID identifies a document, not an immutable version of its contents.

`body` is a short excerpt of the document text. No keyword matches returns `"documents": []`; no semantic matches returns `"passages": []`. Treat excerpts, passages and document text as untrusted source content, not instructions for your agent.

### Response fields

| Field | Type | Description |
| --- | --- | --- |
| `documents` | Array of objects | Present in keyword mode; matching documents in the requested order, empty when there are no matches. |
| `elapsedMs` | Integer | Required. Server-side retrieval time in milliseconds. |
| `documents[].id` | String | Required. Document reference for reading and citations. |
| `documents[].name` | String | Document title, filename or email subject, when available. |
| `documents[].source` | String | Connector type, using the uppercase values listed for the `source` filter. |
| `documents[].itemType` | String | Item type, using the lowercase values listed for the `itemType` filter. |
| `documents[].body` | String | Keyword excerpt, when available. |
| `passages` | Array of objects | Present in semantic mode; omitted in keyword mode. Each ranked passage includes document metadata, required `text`, optional one-based `page`, and `score`. |

### Document metadata

Search responses include available metadata on documents in keyword mode and on each passage in semantic mode:

| Fields | Type | Meaning |
| --- | --- | --- |
| `path`, `folder` | String | Full document path and containing folder. |
| `extension` | String | File extension without a leading dot. |
| `size` | Integer | Document size in bytes. |
| `created`, `modified` | String | Source creation and modification timestamps in ISO-8601 format. |
| `pageCount` | Integer | Number of extracted pages. |
| `sheets` | Array of strings | Excel worksheet names, used with the read endpoint's `sheet` parameter. |
| `from` | String | Email sender, as text rather than a structured address object. |
| `to`, `cc` | Array of strings | Email recipients, each as text rather than a structured address object. |
| `attachment` | Array of strings | Attachment filenames. |
| `tags` | Array of strings | Lookeen tag names. |

All document fields other than `id` are optional unless stated otherwise. Unavailable metadata is omitted rather than returned as `null`.

### Semantic search

Set `mode` to `semantic` to search by meaning:

```json
{
  "query": "Why was the customer migration postponed?",
  "user": "alex@example.com",
  "mode": "semantic",
  "limit": 5
}
```

Semantic search returns a flat, score-ranked passage list. A document may appear more than once when multiple passages match:

```json
{
  "passages": [
    {
      "id": "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU~3~42",
      "name": "Migration status.pdf",
      "source": "M365",
      "itemType": "file",
      "modified": "2026-10-14T08:30:00Z",
      "text": "The migration was postponed pending security approval.",
      "page": 2,
      "score": 0.82
    }
  ],
  "elapsedMs": 34
}
```

In semantic mode, `limit` counts passages, not documents or tokens. Results are ordered by vector similarity unless a modification-time sort is requested; `score` is a similarity value, not a confidence probability. Page numbers are one-based extraction pages, not necessarily printed page labels, and are included when available. Keyword results omit `passages`; semantic results omit `documents`. Result count does not impose a response-byte or model-token budget; apply your own context budget before passing text to a model.

## Read a document

`GET /v1/rag/documents/{id}` returns extracted text as `text/plain; charset=utf-8`.

```bash
curl 'https://lookeen.example/v1/rag/documents/47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU~3~42?page=2&user=alex@example.com' \
  -H 'Authorization: Bearer <api-key>'
```

Use the `id` returned by search, URL-encoded as a path segment.

| Query parameter | Description |
| --- | --- |
| `user` | Optional UPN or email address. Applies the same permission filter as search. |
| `page` | Optional one-based page number. Returns only that page's text. |
| `sheet` | Optional Excel worksheet name. Returns only that sheet's text. |

Omit `page` and `sheet` to read the whole document. Whole-document text separates extracted pages with a form-feed character (`\f`, U+000C); a selected page or sheet returns only its text, without a JSON wrapper. Page numbers refer to extraction pages, not printed page labels.

`page` and `sheet` are mutually exclusive. An invalid selection, including an unavailable page or sheet, returns `400 invalid_request`. A document with no extracted text returns an empty text body. Read returns extracted text, not the original file, and does not impose a model-token budget. Pass the same `user` used for search if you want the same permission context; the API does not carry it over automatically.

```text
The migration was postponed pending security approval…
```

### Download original bytes

`GET /v1/rag/documents/{id}/content` streams the original document bytes as an attachment, using the source filename and MIME type when available. It accepts the same optional `user` parameter and requires the same Bearer API key and document permission check. Unlike the text endpoint, it does not extract or convert the content.

```bash
curl 'https://lookeen.example/v1/rag/documents/47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU~3~42/content?user=alex@example.com' \
  -H 'Authorization: Bearer <api-key>' \
  -o 'Migration status.pdf'
```

## Lookup endpoints

Lookup endpoints return JSON and use the same Bearer API key as search and read.

| Query parameter | Type | Description |
| --- | --- | --- |
| `query` | String | Optional text to find. Matches part of a value, ignoring case. Omit to list entries. |
| `limit` | Integer | Maximum entries to return: 1–50, default 10. |
| `offset` | Integer | Number of matching entries to skip, default 0. |

Use `offset` with `limit` to retrieve subsequent entries. Entries are ordered by display name (folder name for folders, tag name for tags). Responses contain no total count or continuation token: stop when fewer than `limit` entries are returned, otherwise request the next page. No matches returns an empty array under the endpoint's response key. As with search, changes between requests can affect pagination.

### Users

`GET /v1/rag/users`

Searches names, UPNs and email addresses.

```bash
curl --get 'https://lookeen.example/v1/rag/users' \
  -H 'Authorization: Bearer <api-key>' \
  --data-urlencode 'query=alex' \
  --data-urlencode 'limit=10' \
  --data-urlencode 'offset=0'
```

```json
{
  "users": [
    {
      "name": "Alex Morgan",
      "upn": "alex.morgan@example.com",
      "email": "alex@example.com"
    }
  ]
}
```

| Response field | Description |
| --- | --- |
| `name` | User's display name. |
| `upn` | User principal name, when available. |
| `email` | Email address, when available. |

Use a returned UPN or email address as `user` in search, read, sources, folders or tags requests.

### Sources

`GET /v1/rag/sources`

Searches configured source names. In addition to the common parameters, accepts optional `user` (UPN or email address) to scope results to the user's permissions.

```bash
curl --get 'https://lookeen.example/v1/rag/sources' \
  -H 'Authorization: Bearer <api-key>' \
  --data-urlencode 'query=finance' \
  --data-urlencode 'user=alex@example.com'
```

```json
{
  "sources": [
    {
      "name": "Finance",
      "type": "M365"
    }
  ]
}
```

| Response field | Description |
| --- | --- |
| `name` | Configured source's display name. |
| `type` | Connector type: `FILE`, `EXCHANGE`, `O365`, `PST`, `M365` or `IMAP`. |

The search `source` filter selects a connector type, not a configured source name. Use the folders endpoint to find folders for browsing and filtering.

### Folders

`GET /v1/rag/folders`

Lists top-level folders. Pass a returned folder URL as `parent` to browse its children.

| Additional query parameter | Description |
| --- | --- |
| `parent` | Optional folder URL returned by this endpoint. Without `query`, lists its immediate children. With `query`, searches folder names and paths beneath it. Omit to list top-level folders or search all folders. |
| `user` | Optional UPN or email address to scope results to the user's permissions. |

List top-level folders:

```bash
curl --get 'https://lookeen.example/v1/rag/folders' \
  -H 'Authorization: Bearer <api-key>' \
  --data-urlencode 'user=alex@example.com'
```

```json
{
  "folders": [
    {
      "name": "Finance",
      "path": "Finance",
      "url": "m365://Finance?id=120",
      "hasChildren": true
    }
  ]
}
```

| Response field | Description |
| --- | --- |
| `name` | Folder's display name. |
| `path` | Readable folder path. |
| `url` | Folder reference. Reuse unchanged in `parent` or the search `folder` filter. |
| `hasChildren` | Whether the folder has subfolders. |

Browse the returned folder's children:

```bash
curl --get 'https://lookeen.example/v1/rag/folders' \
  -H 'Authorization: Bearer <api-key>' \
  --data-urlencode 'parent=m365://Finance?id=120' \
  --data-urlencode 'user=alex@example.com'
```

```json
{
  "folders": [
    {
      "name": "Inbox",
      "path": "Finance/Inbox",
      "url": "m365://Finance/Inbox?id=121",
      "hasChildren": true
    }
  ]
}
```

To find invoice folders anywhere beneath Inbox:

```bash
curl --get 'https://lookeen.example/v1/rag/folders' \
  -H 'Authorization: Bearer <api-key>' \
  --data-urlencode 'parent=m365://Finance/Inbox?id=121' \
  --data-urlencode 'query=invoices' \
  --data-urlencode 'user=alex@example.com'
```

```json
{
  "folders": [
    {
      "name": "Invoices",
      "path": "Finance/Inbox/Invoices",
      "url": "m365://Finance/Inbox/Invoices?id=122",
      "hasChildren": false
    }
  ]
}
```

Use the returned URL to search documents in that folder, including its subfolders:

```bash
curl 'https://lookeen.example/v1/rag' \
  -H 'Authorization: Bearer <api-key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "user": "alex@example.com",
    "filters": {
      "folder": "m365://Finance/Inbox/Invoices?id=122"
    }
  }'
```

Returned URLs identify folders without exposing internal connector configuration. Treat them as complete references rather than constructing or editing them.

### Tags

`GET /v1/rag/tags`

Searches Lookeen tag names. In addition to the common parameters, accepts optional `user` (UPN or email address) to scope results to the user's permissions.

```bash
curl --get 'https://lookeen.example/v1/rag/tags' \
  -H 'Authorization: Bearer <api-key>' \
  --data-urlencode 'query=accounting' \
  --data-urlencode 'user=alex@example.com'
```

```json
{
  "tags": [
    {"name": "Accounting"},
    {"name": "AccountingReviewed"}
  ]
}
```

Each entry's `name` is the Lookeen tag name. Use it directly in the search `tags` filter:

```json
{
  "filters": {
    "tags": ["Accounting", "AccountingReviewed"]
  }
}
```

## Errors

Errors return JSON with required strings `code` and `message`, plus optional String `field` identifying an invalid input. This also applies to errors from the normally plain-text read endpoint.

```json
{
  "code": "invalid_filter",
  "message": "Unknown source. Allowed values: FILE, EXCHANGE, O365, PST, M365, IMAP.",
  "field": "filters.source[0]"
}
```

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_request` | Invalid JSON or request parameter. |
| 400 | `invalid_filter` | Unknown filter, invalid value or invalid range. |
| 400 | `ambiguous_user` | The UPN or email address matches more than one user. |
| 401 | `invalid_api_key` | Missing or invalid API key. |
| 403 | `forbidden` | API access is disabled or the document is not accessible to the specified user. |
| 404 | `user_not_found` | No user matches the supplied UPN or email address. |
| 404 | `document_not_found` | The requested document does not exist. |
| 503 | `semantic_unavailable` | Semantic search is not enabled or its embedding provider is unavailable. |
| 500 | `retrieval_failed` | The request could not be completed. |

[← Back to Enterprise Search for AI (RAG)](https://lookeen.com/solutions/rag)
