---
title: "Create companies in bulk"
method: POST
path: "/api/v1/companies/batch"
tags: ["Companies"]
---

# Create companies in bulk

`POST /api/v1/companies/batch`

## Request

- Base URL: `https://api.recruitwithatlas.com`
- URL: `https://api.recruitwithatlas.com/api/v1/companies/batch`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/recruitwithatlas/apis/atlas-api/docs/api/v1/companies/batch/post.ts> (a function with typed inputs; its response is checked against the schema)

Creates up to 50 companies under the caller's agency in one request. Each item follows the exact same rules as `POST /api/v1/companies` (including the `websiteUrl`-or-`linkedinUrl` requirement, and the `relationship` default of `none` that keeps a company out of the default `GET /api/v1/companies`).

Items are processed independently and run a few at a time, so they do not necessarily complete in request order; results are always reported in request order via `index`. An item that fails — e.g. its `websiteUrl` / `linkedinUrl` already belongs to an existing company, it references an unknown custom attribute, or an unexpected internal error occurs — is reported in the response with `status: "failed"` and does not abort the remaining items. Internal errors carry a generic `error` message and are safe to retry **only when the message says the item was not processed**; when the failure happened after the company row was already persisted, the error says so and includes the created company id — do not retry that item as-is (its identity now exists, so a retry returns a conflict); verify the company and complete any missing fields via `PATCH /api/v1/companies/{id}` instead. Identity conflicts include the existing company id and should not be retried as-is.

The response is always `200` with one result per input item (matched by `index`, the zero-based position in the request array) plus a `summary` of created/failed counts. A request-level validation error (malformed item, more than 50 items, or the same company named by two items — see the request body) is rejected as a whole with `422` and creates nothing. Created items return the new company `id` only — read full details via `GET /api/v1/companies/{id}`.

## Request body

- object
  - `companies` object[], required: Companies to create (1–50 items). Each item has the same shape as the POST /companies body. A company may be named at most once per batch: two items sharing a normalized websiteUrl or linkedinUrl reject the whole request with 422 before anything is written.
    - `name` string, required: Company name (max 500 characters)
    - `websiteUrl` string, nullable: Company website URL. Stored as a CompanyIdentity of type `website`. Bare hostnames are normalised (`acme.com` → `https://acme.com`). At least one of `websiteUrl` or `linkedinUrl` is required.
    - `linkedinUrl` string, nullable: LinkedIn company URL. Stored as a CompanyIdentity of type `linkedin`. At least one of `websiteUrl` or `linkedinUrl` is required.
    - `summary` string, nullable: Short company summary. Empty strings are rejected — omit or send null to clear.
    - `overview` string, nullable: Long-form company description. Empty strings are rejected — omit or send null to clear.
    - `size` '1-10' | '11-50' | '51-200' | '201-500' | '501-1000' | '1001-5000' | '5001-10000' | '10001+' | 'null', nullable: Company size bucket
    - `industry` string[], nullable: Industry labels. A label that matches the Atlas industry dictionary (matched case-insensitively on the whole label) is stored in the dictionary’s canonical casing so the `industry` filter matches it; any other label is stored exactly as sent. Duplicate entries are silently dropped (`["Tech","Tech"]` → `["Tech"]`). Note `PATCH /companies/{id}` is stricter — it rejects labels outside the dictionary.
    - `relationship` 'client' | 'target' | 'none': Company relationship: `client` — an active client; `target` — a company you are targeting for new business; `none` — no commercial relationship (the default, and the value of companies auto-created from a person’s work history). `target` is a company-only value: a contact’s `relationship` uses `prospect` for the same idea. See "Relationship values" in the introduction. **Defaults to `none` when omitted, and `none` companies are excluded from the default `GET /api/v1/companies`** — a company created without this field is not returned by the list endpoint unless you ask for it explicitly (`?relationship=none` or `?relationship=client,target,none`). Send `client` or `target` on create to have the company appear in the default list.
    - `type` 'educational' | 'government' | 'nonprofit' | 'private' | 'public' | 'null', nullable: Company type
    - `employeeCount` integer, nullable: Number of employees (precise, integer)
    - `ticker` string, nullable: Stock ticker symbol. Empty strings are rejected — omit or send null to clear.
    - `location` object, nullable: Location
      - `name` string, nullable: Display name / formatted address (e.g. "London, UK")
      - `locality` string, nullable: City name
      - `region` string, nullable: State or region
      - `metro` string, nullable: Metro area (People Data Labs convention, e.g. "new york, new york")
      - `country` string, nullable: Country name
      - `streetAddress` string, nullable: Street address line 1
      - `addressLine2` string, nullable: Street address line 2 (apartment, suite, unit, etc.)
      - `postalCode` string, nullable: Postal / ZIP code
      - `raw` string, nullable: Raw unstructured address text (used for geocoding)
      - `latitude` number, nullable: Latitude coordinate (-90..90)
      - `longitude` number, nullable: Longitude coordinate (-180..180)
    - `customAttributes` object[]: Custom attribute values. For options-type attributes with multipleValues=true, repeat the same customAttributeId with distinct optionIds.
      - `customAttributeId` string, uuid, required: Custom attribute definition ID — must belong to the agency at the correct scope.
      - `optionId` string, uuid, nullable: Required for `options`-type attributes — set to the chosen option's UUID. Mutually exclusive with `value`. For multi-select (`multipleValues: true`), repeat the entry once per chosen optionId.
      - `value` union: Attribute value. Shape depends on the attribute `type`: - `text_line` / `text_block`: non-empty string (trimmed, max 16384 chars). - `integer` / `number_input`: JSON number, integer only, must fit Postgres int32 (-2147483648..2147483647). - `date`: string `YYYY-MM-DD`, calendar-validated. - `options`: do not send `value` — use `optionId` instead. Mutually exclusive with `optionId`. Sending neither is rejected (422).
        - string
        - number

## Response `200`

Batch processed — check each result item (and the summary) for per-item outcomes

- object
  - `status` 'ok', required
  - `data` object[], required
    - `index` integer, required: Zero-based position of the item in the request `companies` array
    - `status` 'created' | 'failed', required: Outcome for this item
    - `id` string, uuid: Created company id (present when status is `created`)
    - `error` string: Failure reason (present when status is `failed`)
  - `summary` object, required
    - `total` integer, required: Number of items in the request
    - `created` integer, required: Number of companies created
    - `failed` integer, required: Number of items that failed

## Other responses

- `401`: Unauthorized - missing or invalid API key
- `422`: Validation error - the request failed schema validation (`errors.fieldErrors`) or a business rule (`errors.formErrors`)
- `429`: Too many requests - the caller has exceeded the per-agency rate limit for the tier this endpoint counts against (default per minute: 1200 read / 400 write / 60 upload). Inspect the `RateLimit-*` headers — returned on every response, not only on 429s — and back off until the window resets. See the "Rate limits" section of the introduction for details.
- `500`: Internal server error

## Changes

- **2026-11-03** `158ee454751c`: 2 info
  - added the new `undefined` enum value to the request property `companies/items/size`
  - added the new `undefined` enum value to the request property `companies/items/type`
- **2026-10-03** `cd4bc8bbd95e`: 1 info
  - added the optional property `error` to the response with the `422` status
- **2026-09-29** `d7ef7f970d5b`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/recruitwithatlas/apis/atlas-api/changes/api/v1/companies/batch/post.md)

---

[API](https://skmtc.dev/recruitwithatlas/apis/atlas-api.md) - [All operations](https://skmtc.dev/recruitwithatlas/apis/atlas-api/llms.txt) - [OpenAPI document](https://skmtc.dev/recruitwithatlas/apis/atlas-api/revisions/158ee454751c?raw)
