Skip to main content
Use POST /api/v2/filings/search to search filing documents or browse by metadata. You need an API key with permission to view SERFF filings. Readonly keys can use this operation.

Search document text

Set API_BASE to your API host (for example, https://canary.effectiveai.app) and EFFECTIVE_API_KEY to your API key, then run:
The response has a filings array of filings, a nullable nextCursor, and an exhaustive flag. Each filing includes its ID, source and native reference, company, state, classifications and dates. By default, text results include matches from the configured snippet mode. The optional textFetchMode uses the values: docReader, fromTurbo, or none. Use none to skip text hydration; document matches may still be returned without passages. Each match contains the file ID, nullable version ID, name, and passages with available page numbers. Missing metadata and page numbers are null. No matching filings returns an empty filings array. Filters combine with AND. Values within in and containsAny combine with OR. Use the normalized toiCode, subToiCode, normalizedOutcome, and filingTypeNormalized values defined in the API reference. Raw insurance classification labels and status strings in the response may vary by source. Text/identifier queries default to recent filings (2018+ and undated); metadata-only requests default to all dates. Set dataScope explicitly to historical, recent, or all as needed. Carrier filters use carrierIds (containsAny/notContainsAny) and canonical naic:00123 IDs or numeric shorthand strings. Group equality uses groupId, for example group:361. State values normalize to uppercase. Search’s trackingNumber filters and sorts use backend reference spelling (including FL- for IRFS); ordinary GET discovery accepts native source-qualified references. A match’s versionId is currently null: the backend does not identify the exact immutable bytes behind the evidence. Current downloads may differ from indexed text. Do not treat today’s file version as the evidence version. To match an exact phrase, quote it inside query, for example "query": "\"roof exclusions\"". Exact phrases require a state, company, NAIC, tracking-number or date filter to narrow the search. Exclusion filters alone do not qualify. Without a narrowing filter, the shared search service removes the quotes and performs keyword search.

Browse without document text

Omit query to list filings by metadata alone. For example, replace the request body above with:
Both text search and browsing default to submission date descending, with a page size of 50 (maximum 200). To rank text matches by relevance, explicitly set sort to [{"field":"relevance","direction":"desc"}]. Browsing does not return snippets. Tracking-number queries use the existing identifier lookup: complete identifiers match exactly; partial identifiers use prefix matching with up to 10 results. Metadata filters and data scope apply before the lookup result limit. Metadata sorts use filing ID ascending as a final tie-breaker. Within text results, missing values sort last; metadata-only sorting puts missing values first for descending sorts and last for ascending sorts. Relevance retains backend score order, including backend tie behavior. Neither ordering freezes membership. When nextCursor is non-null, repeat the same request with that value in cursor. Keep the query, filters, data scope, sorting, text-fetch mode and page size unchanged. Opaque server-held cursors bind the normalized query and caller scope and expire 24 hours after the first page; continuation never extends this lifetime. Tampered, expired, or differently bound cursors return 400 invalid_cursor. Equivalent explicit defaults and reordered set-valued filters continue successfully. The private position is a live backend offset, not a saved search. Every page reruns the query; index/ranking changes and concurrent edits may cause repeats or omissions. Deduplicate IDs and restart when a fresh view is needed. Prefer GET browsing for its keyset behavior when the simpler metadata filters suffice. Text search returns a bounded candidate window, with exhaustive: false. With textFetchMode: "docReader" or "fromTurbo", search retrieves at most 100 candidate documents. With "none" or an omitted mode, it requests up to 1,200. An omitted mode uses the configured text-fetch strategy. Several documents can belong to one filing, so the number of filings can be smaller. Metadata sorting applies within this window. A null cursor means the available window is exhausted, and does not prove no more corpus matches exist. Narrow your filters to investigate more specific results. No exact corpus total is returned. Metadata browsing returns exhaustive: true: pagination can traverse the matching metadata records. This describes the available dataset, not completeness of every state’s public filings or document indexing coverage.

Follow a result to bytes

Use IDs returned by search and the file inventory; never derive them from names.
Follow inventory cursors until null and download selected files with bounded concurrency. Use an inventory version when present to pin a download, while keeping its identity distinct from the unknown search-evidence version. Follow the download redirect. Retrying the same version can renew its short-lived URL; a deleted/unavailable version must fail rather than substitute current bytes. Metadata-only IRFS can return no stored files; reads never trigger acquisition.

Handle errors

API-generated failures include error and code; responses are private and not cacheable. JSON requests are limited to 64 KiB, reject unknown body/query fields, and require application/json.
  • 400 invalid_request: correct filters, dates, sorting or malformed JSON.
  • 400 invalid_cursor: restart with the original semantic request and no cursor.
  • 401 unauthenticated: check the API key.
  • 403 forbidden: the caller needs current Filing access within its credential scope.
  • 413 request_too_large / 415 unsupported_media_type: correct the request body.
  • 503 temporarily_unavailable: respect Retry-After; narrow expensive queries before retrying timeouts. Cursor-store failure does not silently drop continuation.
  • 500 internal_error: unexpected failure; no backend details are exposed.