Contents

Search

Find records by name, matter or the words inside.

The FolioSearchResult object

Attributes

  • scope required Type: "names" "content"

    What was searched.

  • records required 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}]}]) and low_confidence.

  • matched_records optional Type: integer

    Content scope only.

  • matched_pages optional Type: integer

    Content scope only.

  • not_searchable optional Type: integer

    Content scope only. Records in scope whose text is not extracted yet, or failed.

  • low_confidence optional Type: integer

    Content scope only. Records in scope whose text was read with low confidence.

  • snippets optional Type: boolean

    Content scope only. Whether snippets were shown.

FolioSearchResult on its own page

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

  • q queryrequired Type: string
  • scope queryoptional Type: "names" "content"
  • folder queryoptional Type: string
  • matter queryoptional Type: string

    Content scope only.

  • subject queryoptional Type: string

    Content scope only.

  • class queryoptional Type: string

    A retention class id (content scope only).

  • lock queryoptional Type: "retained" "locked"

    Content scope only.

  • from queryoptional Type: integer

    Filed at or after, Unix milliseconds (content scope only).

  • to queryoptional Type: integer

    Filed before, Unix milliseconds (content scope only).

  • held queryoptional Type: boolean

    Only records under a legal hold, or only records not under one (content scope only).

  • limit queryoptional Type: integer

    At most 200 for names, 50 for content.

Returns

  • 200 Type: FolioSearchResult
    Matching records
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /folio/search · curl
curl "https://api.unforged.sh/folio/search?q=<q>" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /folio/search · Python
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())
GET /folio/search · Node.js
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());