# Batching

> Every lookup and reveal takes an array of 1 to 25 items and answers each one separately, in order, with your ref echoed back.

There are no single-item endpoints. Matching, contacts, switchboards and reveals all take `items`, an array of 1 to 25 objects, and return one result per item in `results`. One item is just a batch of one.

```json
{
  "items": [
    { "ref": "crm-2001", "linkedInUrl": "https://www.linkedin.com/in/nordvik" },
    { "ref": "crm-2002", "domain": "nordviklogistik.se" }
  ]
}
```

```json
{
  "results": [
    {
      "ref": "crm-2001",
      "status": "invalid",
      "error": {
        "code": "invalid_value",
        "message": "linkedInUrl must be an organization's LinkedIn page, https://www.linkedin.com/company/…"
      }
    },
    {
      "ref": "crm-2002",
      "status": "matched",
      "matchedBy": "domain",
      "organization": {
        "id": "6f9d2f1e-6f37-4a2f-9a3e-1b5c0f4d7e21",
        "name": "Nordvik Logistik AB",
        "detailsRevealed": false,
        "countryCode": "SE",
        "domain": "nordviklogistik.se",
        "city": "Göteborg",
        "legalForm": "Aktiebolag",
        "foundedYear": 2004,
        "url": "https://app.funnelfeedr.com/organizations/6f9d2f1e-6f37-4a2f-9a3e-1b5c0f4d7e21"
      }
    }
  ],
  "creditsCharged": 0,
  "creditsQuoted": 0,
  "creditsRemaining": 412.4,
  "dryRun": false
}
```

## The rules

- **1 to 25 items.** An empty or missing array fails the whole request with `422 validation_failed`; more than 25 with `422 too_many_items` (the body carries `maxItems: 25`). Split larger jobs into several requests.
- **Same order.** `results[i]` answers `items[i]`, always.
- **`ref` is yours.** Optional, at most 200 characters — a CRM id, a row number. It comes back unchanged so you never have to rely on order alone. Funnelfeedr does not store it.
- **A status per item.** Each result has a `status`. A bad item gets `"status": "invalid"` with an `error`, and the rest of the batch still runs. A `200` response therefore does not mean every item worked: check each `status`.
- **Malformed JSON fails the whole request.** A value of the wrong type — an `id` that is not a UUID, say — is caught before any item runs: `422 validation_failed`, with the field path in `errors` (`"items[3].organization.id": ["Must be a UUID."]`).
- **One request, one charge.** A batch that reveals is quoted, capped and charged as a whole. See [Credits & reveal](https://funnelfeedr.com/developers/credits-and-reveal.md).

## Item errors

An `invalid` item's `error` has a stable `code` and a human-readable `message` naming the field to fix:

| `code` | Meaning |
|---|---|
| `missing_identifier` | The item names nothing to look up. An empty or blank field counts as not sent. |
| `invalid_value` | A field is present but unusable: an email without `@`, a LinkedIn URL of the wrong kind, a name with no letters in it, a `ref` over 200 characters. |
| `missing_organization` | `people/match`: a name was given without an `organization`. |
| `missing_id` | No id on a reveal item. |
| `invalid_cursor` | `organizations/contacts/match`: a `cursor` that was altered, or that belongs to another organization than the item's identifiers name. |

Fix the item and send it again on its own or in your next batch; the other items already have their answers.

## Large jobs

For a CRM of 10,000 organizations, send 400 requests of 25. They count against your [rate limits](https://funnelfeedr.com/developers/rate-limits.md) like any other request, so pace them: the matching endpoints, organizations' contacts included, share the `search` bucket of 20 requests a minute by default, which at 25 items per request is 500 items a minute. Honour `Retry-After` if you get a `429`.

There are no asynchronous bulk jobs, file uploads or webhooks in v1. Every request answers synchronously.
