---
title: "Analyze up to 50 documents in parallel"
method: POST
path: "/api/v1/analyze/batch"
tags: ["Analysis"]
---

# Analyze up to 50 documents in parallel

`POST /api/v1/analyze/batch`

## Request

- Base URL: `https://legal.nexusquantum.legal`
- URL: `https://legal.nexusquantum.legal/api/v1/analyze/batch`
- Auth: HTTP bearer

Processes an array of up to 50 legal documents in parallel (configurable concurrency 1-10). Returns results in the same order as the input array, with `status: success` or `status: error` per document.

Credits: worst-case pre-check (`documents.length` credits). Post-processing charges only for `status: success`. If the balance is not enough for the full batch, it returns 402 without processing anything.

Rate limit: 1 batch = 1 hit against RPM (credits do scale linearly).

Supports `Idempotency-Key` applied to the whole batch (hash of the array). 24h cache.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `documents` object[], required: Array of documents to analyze.
    - `id` string: Optional ID echoed back in the response for correlation.
    - `text` string, required
    - `jurisdiction` string, required: ISO code. REQUIRED per document — no default (400 MISSING_JURISDICTION if missing).
    - `legalBranch` string
    - `language` 'es' | 'en': Optional — if omitted, the language follows the document jurisdiction (SG→en, ES/CO→es, …).
    - `mode` 'standard' | 'deep': Compat aliases accepted: agil ⇄ standard, auditoria ⇄ deep. An invalid mode returns 400 INVALID_MODE.
  - `mode` 'standard' | 'deep': Mode override for ALL documents in the batch. Compat aliases accepted: agil ⇄ standard, auditoria ⇄ deep. An invalid mode returns 400 INVALID_MODE.
  - `concurrency` integer: Simultaneous parallel analyses.

## Response `200`

Batch completed. Individual documents may come back with `status: error`.

- object
  - `batchId` string, uuid
  - `results` union[]
    - union
      - object
        - `id` string, nullable
        - `status` 'success'
        - `analysis` string
        - `jurisdiction` string
        - `legalBranch` string
        - `model` string
        - `processingMs` integer
        - `citationChecks` CitationVerificationSummary
          - `checks` CitationCheck[], required
            - `citation` string, required: Citation exactly as it appeared in the analysis.
            - `kind` 'L4-N' | 'L2-J', required: Legislation (L4-N) or case law (L2-J).
            - `status` 'verified' | 'mismatch' | 'not_found' | 'derogated' | 'pending' | 'not_verifiable', required: verified=exists and matches; mismatch=exists but diverges; not_found=absent from the active corpus; not_verifiable=jurisdiction with no active corpus; pending=not parseable.
            - `similarity` number, nullable, required: Lexical similarity (Jaccard) against the canonical text.
            - `semantic` number, nullable, required: Semantic similarity (cosine), only in the lexical gray zone.
            - `url` string, nullable, required: Official source URL for THAT jurisdiction when one is located (BOE · CENDOJ · Singapore Statutes Online · eLitigation · SUIN-Juriscol…).
            - `mismatchKind` 'meaning' | 'stitched' | 'lexical' | 'party': Only with `status: mismatch`, and ADDITIVE (may be missing from older checks). Grades the severity, which used to be lost: `meaning` = a meaning veto fired (negation, modality, quantifier, omitted exception, subject) → a REAL divergence of meaning. `stitched` = the quoted fragments are literal but do not come from a CONTINUOUS passage of the official text, or they alter its order — an ASSEMBLED quote. The defect is structural: a divergence of meaning has NOT been proven, so it must not be presented as one (added 2026-07-25; measured by contiguity since 2026-07-27). `lexical` = only the lexical ratio failed: an honest paraphrase that cannot be checked literally. `party` = the citation EXISTS but resolves to a DIFFERENT decision from the one the text names (incompatible party names — typically a transposed court, e.g. `Spandeck … [2007] SGHC 37` when the case is SGCA 37). It is neither a non-existent citation nor a divergence of meaning: it points elsewhere. `similarity` comes back `null` on purpose — the claim is not compared against a decision that is not the one named (added 2026-07-27).
            - `note` string, nullable: Human-readable note from the verifier (deterministic, server-side — never from the model). E.g. STATUTE-LEVEL verification of a law cited without an article, or the notice that the registry proved existence without returning the text.
            - `canonicalText` string, nullable: Official text the verifier downloaded to check the citation against, when the corpus returns it. For case law this is the full judgment: truncate it before displaying.
          - `counts` object, required: Count per status.
          - `anyBroken` boolean, required: There is at least one broken citation (mismatch/not_found/derogated). EXCLUDES `pending` (not parseable) and `not_verifiable` (jurisdiction with no active corpus): those statuses are NOT breakages. READ `composition` BEFORE ACTING ON THIS: `anyBroken: false` means every citation is faithful to its source ONE BY ONE. It does NOT mean the argument they support holds — the inferential step that joins them is not verified by this field.
          - `composition` object: SCOPE OF THE COMPOSITION — what `anyBroken` cannot tell you. Verifying each citation proves fidelity to its source; it says nothing about the step that JOINS them. Two impeccable citations can support a false conclusion. This object carries the axes of that step that ARE decidable without understanding the argument — repealed premises, premises not yet in force, and above all validity windows that never overlapped (a defect invisible when citations are checked one at a time) — and, just as important, the axes that are NOT checked, each with its reason. IT IS ALWAYS PRESENT, including when there are no findings: if it appeared only on problems, its absence would again mean "this is fine", which is the reading it exists to prevent. `no_comprobado` permanently includes `validez_logica`: WHETHER THE CONCLUSION FOLLOWS FROM THE PREMISES IS NOT CHECKED, and that is not a pending task — it is not a mechanically decidable question. An empty `hallazgos` is not a statement that the reasoning is valid.
            - `comprobado` string[]: Axes actually examined. An axis only enters here if there was MATERIAL for it to produce a finding — never merely because the code ran.
            - `hallazgos` object[]: ANOMALIES. Named, never an aggregate boolean. A fact only qualifies as a finding if it is measurably RARE in correct work — see `observaciones` for the ones that are not.
              - …
            - `observaciones` object[]: FACTS ABOUT THE SET THAT ARE NOT ANOMALIES, each carrying its measured base rate. Citing authority from another British forum fires on 49.71% of real Scottish and Northern Irish judgments, and in 18.21% every authority cited is from another forum (7,494 judgments, 23,334 resolved citations, measured 2026-08-15) — it is normal practice, so reporting it as a finding would train integrators to ignore the findings that matter. Treat these as checklist items, not defects. `hallazgos: []` WITH a populated `observaciones` is a NORMAL result, not a clean one.
              - …
            - `no_comprobado` object[]: Axes not examined and why. `validez_logica` is permanently here and is NOT decidable; the rest appear only when the data for them was absent, each with its reason.
              - …
            - `sin_dato` object[]: Citations left out of a comparison for lack of data. A missing datum never becomes a finding.
              - …
          - `total` integer, required: Number of verifiable citations detected.
          - `error` boolean: ADDITIVE and optional; present (and always `true`) ONLY when the deterministic verification COULD NOT RUN at all (exception, timeout, corpus unreachable). READ IT BEFORE `anyBroken` or `counts`: with `error: true` the summary carries `total: 0`, `anyBroken: false` and all counters at zero — that is FILLER, not a verdict. It means "the citations were NOT checked", NOT "the citations were checked and are clean". Treat every citation in the answer as unverified and review it against the official source. It is a new optional FIELD on purpose, never a new value inside the closed `status` enum, so integrators that `switch` on status do not break. Absent on every successful verification.
          - `unverifiableClaims` integer: ADDITIVE and optional (present only when > 0). Number of legal assertions written in PROSE that invoke authority without a formal, parseable citation ("as the Court held in Vega, the appeal was dismissed"). No parser extracts them, so they never appear in `checks` and never count as broken — but with `total: 0` they are the difference between "no citations" and "authority claimed with nothing to check it against". Use it for a caution, never to mark a citation verified or not_found.
          - `coverage` object: WHAT COULD BE LOOKED AT, not just what was found. Read this BEFORE `counts` or `anyBroken`. Three situations used to reach the client indistinguishable: a report with no authorities to verify, a report whose citations were all checked, and a report that ASSERTS doctrine with no recognisable citation apparatus — so nothing could be checked at all. The third one is the dangerous one and it is what any prose without citation apparatus produces.
            - `state` 'checked' | 'partially_checked' | 'no_authority' | 'unverifiable' | 'not_measured' | 'check_failed', required: `checked` = there were citations and they were contrasted (see `checks`), and nothing else in the text asserts authority. `partially_checked` = there were citations AND ALSO statements attributing holdings to courts with no checkable reference of their own. The green verdict on the citations does NOT cover those: a single verifiable citation anywhere in the document (an appendix table of authorities, say) must not be readable as a verdict on the whole body. `no_authority` = neither citations nor authority claims: there was genuinely nothing to verify. `unverifiable` = the text attributes holdings or doctrine to courts WITHOUT any checkable reference, so NOTHING was contrasted against the corpus. This is NOT a statement that the content is wrong — it is a statement that it is unverified, which is not the same as a clean report. `not_measured` = no citation found AND the report's language has no authority-claim lexicon, so the zero is a not-measured, not a "none". `check_failed` = the verifier itself could not run (mirrors `error: true`).
            - `citations` integer, required: Citations the verifier was able to look at (equals `total`).
            - `authorityClaims` integer, required: Statements attributing a holding or settled doctrine to an authority with no checkable citation in the same sentence.
            - `examples` string[]: Up to three of those statements, truncated. Present only when there are any: "there are 4" without saying which is not actionable.
            - `language` string, required: Language the report was produced in, as resolved by the platform. Decides whether a zero in `authorityClaims` is measurable.
      - object
        - `id` string, nullable
        - `status` 'error'
        - `error` string
        - `code` 'VALIDATION_ERROR' | 'MODEL_ERROR' | 'UNKNOWN_ERROR'
        - `httpStatus` integer
  - `totals` object
    - `submitted` integer
    - `succeeded` integer
    - `failed` integer
    - `creditsCharged` integer
    - `processingMs` integer
    - `concurrency` integer

## Other responses

- `400`: MISSING_JURISDICTION (`documents[i].jurisdiction` is required — no default) | INVALID_JURISDICTION | INVALID_MODE | VALIDATION_ERROR | INVALID_JSON
- `402`: Insufficient credits for the full batch (worst-case `documents.length` credits).
- `409`: Conflict. `BYO_PROVIDER_REQUIRED` — BYO-mandatory account with no configuration enabled. The WHOLE batch is rejected (enforcement is per firm: all documents share the same firm) without charging for any document. Also returned when an `Idempotency-Key` is reused with a different body.
- `422`: `BATCH_TOO_LARGE` if it exceeds 50 documents.

---

[API](https://skmtc.dev/nexusquantum/apis/nexus-legal-api.md) · [All operations](https://skmtc.dev/nexusquantum/apis/nexus-legal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/nexusquantum/apis/nexus-legal-api/revisions/4084d8d4eb0b?raw)
