Search
Find records by name, matter or the words inside.
The FolioSearchResult object
Attributes
scoperequired Type:"names""content"What was searched.
recordsrequired Type:object[]Record listings. In a content search each also has
pages(matching pages, best first),snippets(null without folio:documents:read; otherwise[{page, parts: [{text, hit}]}]) andlow_confidence.matched_recordsoptional Type:integerContent scope only.
matched_pagesoptional Type:integerContent scope only.
not_searchableoptional Type:integerContent scope only. Records in scope whose text is not extracted yet, or failed.
low_confidenceoptional Type:integerContent scope only. Records in scope whose text was read with low confidence.
snippetsoptional Type:booleanContent scope only. Whether snippets were shown.
Search records
GET /folio/search
Needs folio:documents:search: Search record metadata and document text (audited)
scope=names (the default) searches names, matters and subjects. scope=content searches inside the documents: words (all required), "exact phrases", "two words"~5 for proximity, OR, AND, NOT or a leading -, parentheses, wildcards (regist*, wo?en) and fuzzy words for OCR errors (registry~, registry~2), with metadata filters. Content hits list their matching pages and, for callers who may view documents, snippets cut from the extracted text. The result counts the records in scope that are not yet searchable or were read with low confidence; finding nothing is not proof that a word is absent. The words inside are read from PDFs, scans and photos, plain text, Word, Excel and PowerPoint files, OpenDocument and RTF files, and emails (.eml, with the names of their attachments). Older Office files (.doc, .xls, .ppt), Outlook .msg files, zip archives and password-protected documents are found by name, matter and subject only. No reason is needed; the search, its filters and everything shown are ledgered.
Parameters
qqueryrequired Type:stringscopequeryoptional Type:"names""content"folderqueryoptional Type:stringmatterqueryoptional Type:stringContent scope only.
subjectqueryoptional Type:stringContent scope only.
classqueryoptional Type:stringA retention class id (content scope only).
lockqueryoptional Type:"retained""locked"Content scope only.
fromqueryoptional Type:integerFiled at or after, Unix milliseconds (content scope only).
toqueryoptional Type:integerFiled before, Unix milliseconds (content scope only).
heldqueryoptional Type:booleanOnly records under a legal hold, or only records not under one (content scope only).
limitqueryoptional Type:integerAt most 200 for names, 50 for content.
Returns
200Type:FolioSearchResultMatching records400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl "https://api.unforged.sh/folio/search?q=<q>" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/folio/search?q=<q>",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/folio/search?q=<q>', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());