> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usecroma.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch requests

> Resolve many lookups in one call, with per-item results and partial failure.

Some lookups are naturally done in bulk: you have a list of radicados, cédulas,
or NITs and want them all at once. Batch endpoints take an array of inputs,
resolve them **concurrently** on Croma's side, and return one result per item,
so you make a single call instead of looping.

<Note>
  Batch is an addition, not a replacement. The single-item endpoint stays the
  simplest path for one lookup. Reach for batch when you have a list.
</Note>

## Which endpoints support batch?

| Source                                                        | Batch endpoint                                      |
| ------------------------------------------------------------- | --------------------------------------------------- |
| [Rama Judicial](/guides/colombia/rama-judicial) (by radicado) | `POST /co/rama-judicial/cases-by-radicado/v1/batch` |

More batch endpoints are rolling out. Each follows the exact shape on this page,
so once you integrate one you've integrated them all.

## Request shape

A batch body is always `{ "items": [ … ] }`, where each item is the **same body
the single endpoint takes**. Send between **1 and 50** items.

```bash theme={"dark"}
curl https://api.croma.run/co/rama-judicial/cases-by-radicado/v1/batch \
  -H "Authorization: Bearer $CROMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "items": [
        { "registration_number": "11001600001720180327700" },
        { "registration_number": "05001310300120190012300" }
      ] }'
```

## Response shape

A batch always returns `200` with a `results` array (in the same order as your
`items`) and a `summary`:

```json theme={"dark"}
{
  "data": {
    "results": [
      {
        "index": 0,
        "status": "completed",
        "cache_hit": false,
        "data": { "found": true, "registration_number": "11001600001720180327700", "primary_case": {  }, "actions": [  ] }
      },
      {
        "index": 1,
        "status": "error",
        "error": { "type": "upstream_error", "code": "rama_judicial_upstream", "message": "The Rama Judicial lookup could not be completed." }
      }
    ],
    "summary": { "total": 2, "completed": 1, "errors": 1, "cache_hits": 0 }
  }
}
```

| Field                 | Meaning                                                                                                        |
| --------------------- | -------------------------------------------------------------------------------------------------------------- |
| `results[].index`     | 0-based position in your `items` array. Results are returned in input order.                                   |
| `results[].status`    | `completed` or `error` for that item.                                                                          |
| `results[].data`      | The single endpoint's normal `data` payload. Present when `status` is `completed`.                             |
| `results[].cache_hit` | `true` when that item was served from cache. Present when `status` is `completed`.                             |
| `results[].error`     | `{ type, code, message }`, the same error shape the single endpoint returns. Present when `status` is `error`. |
| `summary`             | Counts across the batch: `total`, `completed`, `errors`, `cache_hits`.                                         |

## Partial failure

A single bad item never fails the whole batch. If one radicado's upstream lookup
errors, that item comes back with `status: "error"` while the rest still resolve.
Always iterate `results` and check each item's `status`.

There are two distinct layers to handle:

<Note>
  **Validation errors fail the whole request; upstream errors are per-item.**

  * A malformed item (for example a `registration_number` that isn't 20-25
    digits) is rejected up front with a `400` and a `param` like
    `items.0.registration_number`. Nothing runs.
  * A well-formed item whose upstream lookup fails comes back inside `results`
    as `status: "error"`, with the batch itself still `200`.
</Note>

```ts TypeScript theme={"dark"}
const res = await fetch(
  "https://api.croma.run/co/rama-judicial/cases-by-radicado/v1/batch",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CROMA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      items: [
        { registration_number: "11001600001720180327700" },
        { registration_number: "05001310300120190012300" },
      ],
    }),
  },
);

if (res.status === 400) {
  // a malformed item; nothing ran. Fix it and resend.
  throw new Error("invalid batch body");
}

const { data } = await res.json();
for (const item of data.results) {
  if (item.status === "completed") {
    handle(item.data); // your normal per-lookup logic
  } else {
    console.warn(`item ${item.index} failed:`, item.error.message);
  }
}
```

## Limits and behavior

* **Up to 50 items** per request. More than that is rejected with a `400`.
* **Each item counts as one request** against your quota. A 10-item batch
  consumes 10 from your [rate limit](/rate-limits), the same as 10 single calls,
  so a batch that would exceed your remaining quota returns `429`.
* **Duplicates are deduped.** The same input twice in one batch is fetched once;
  both positions get the result.
* **Cache is shared** with the single endpoint. An item you looked up recently
  (single or batch) comes back with `cache_hit: true`.

<Card title="Rate limits" icon="gauge-high" href="/rate-limits">
  How per-item quota and the `X-RateLimit-*` headers work.
</Card>

<Card title="Errors" icon="triangle-exclamation" href="/errors">
  The error envelope and every error code.
</Card>
