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

# Import users

> POST /v1/platform-users/import: create or update up to 1,000 platform users with their existing wallets in one call.

Creates or updates up to 1,000 [platform users](https://docs.cabalspy.xyz/cabalspy-id/white-label.md) in one call, for example to bring your existing user base and their Privy or Dynamic wallets over. Each user is handled exactly like [Create or update a user](https://docs.cabalspy.xyz/cabalspy-id/platform-users-create.md).

Each entry is handled on its own: one invalid entry, or reaching your plan's user limit, never undoes the others. The response lists the result of every entry in the order you sent them.

> [!TIP]
>
> At most 1,000 users per call and a request body of up to 1 MB. Split larger user bases into several calls. Re-running an import is safe: unchanged users are reported as `unchanged`.

## Body

**users** `array` *required*

1 to 1,000 users.

**user**

**external\_id** `string` *required*

Your id of the user. 1 to 128 printable ASCII characters, no spaces.

**wallets** `array`

Up to 10 wallets, objects with address, chains and source, or plain addresses. Required for new users. See [Create or update a user](https://docs.cabalspy.xyz/cabalspy-id/platform-users-create.md#body).

**display\_name** `string`

Display name, 1 to 60 characters, or null.

**avatar\_url** `string`

Avatar as an https URL, or null.

## Usage Example

```bash cURL theme={null}
curl -X POST "https://api-id.cabalspy.xyz/v1/platform-users/import" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "users": [
      {"external_id": "user_8412", "display_name": "Maze",
       "wallets": [{"address": "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW", "source": "privy"}]},
      {"external_id": "user_8413", "wallets": ["0x4b1e9c0d2a7f3e5b8c6d1a9f0e2b7c4d5a6e8f01"]},
      {"external_id": "user_8414"}
    ]
  }'
```

```python Python theme={null}
import requests

H = {"Authorization": "Bearer YOUR_KEY"}
batch = [{"external_id": u["id"], "wallets": u["wallets"]} for u in my_users]
for i in range(0, len(batch), 1000):
    r = requests.post("https://api-id.cabalspy.xyz/v1/platform-users/import",
                      json={"users": batch[i:i + 1000]}, headers=H).json()["data"]
    print(r["summary"])
    for res in r["results"]:
        if res["status"] == "rejected":
            print(res["external_id"], res["error"]["code"], res["error"]["message"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/platform-users/import", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ users: myUsers.slice(0, 1000) }),
});
const { data } = await res.json();
console.log(data.summary);
data.results.filter(r => r.status === "rejected").forEach(r => console.log(r.external_id, r.error.code));
```

## Response

**data** `object`

**data**

**summary** `object`

Counts per status: created, updated, unchanged and rejected.

**results** `array`

One result per entry, in the order sent.

**result**

**external\_id** `string`

The entry's external\_id, as sent.

**csid\_id** `string`

The user's CabalSpy ID account id. Not present for rejected entries.

**status** `string`

`created`, `updated` (wallets added or a profile field changed), `unchanged` (nothing new, also when the same display\_name or avatar\_url was passed again) or `rejected`.

**wallets\_added** `array`

Wallets and chains that are new: address, family, chains and source. Not present for rejected entries.

**error** `object`

Rejected entries only: code and message, the same codes as [Create or update a user](https://docs.cabalspy.xyz/cabalspy-id/platform-users-create.md#errors).

Once an entry hits your plan's user limit, every following entry for a **new** user is rejected with `plan_user_limit` as well. Entries for existing users are still updated.

```json Response theme={null}
{
  "success": true,
  "data": {
    "summary": { "created": 2, "updated": 0, "unchanged": 0, "rejected": 1 },
    "results": [
      {
        "external_id": "user_8412",
        "csid_id": "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718",
        "status": "created",
        "wallets_added": [
          { "address": "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW", "family": "solana", "chains": ["solana"], "source": "privy" }
        ]
      },
      {
        "external_id": "user_8413",
        "csid_id": "8e2b4d6f-1a3c-4e5f-9b7d-0c2e4a6b8d10",
        "status": "created",
        "wallets_added": [
          { "address": "0x4b1e9c0d2a7f3e5b8c6d1a9f0e2b7c4d5a6e8f01", "family": "evm", "chains": ["base", "bnb", "eth", "rh"], "source": null }
        ]
      },
      {
        "external_id": "user_8414",
        "status": "rejected",
        "error": {
          "code": "wallet_required",
          "message": "Every user needs at least one wallet: pass wallets: [{\"address\": \"...\"}]."
        }
      }
    ]
  },
  "meta": {
    "request_id": "req_5162c7cf7f87",
    "cached": false,
    "cache_age_seconds": 0,
    "version": "v1",
    "timestamp": "2026-10-24T09:12:41Z"
  }
}
```

## Errors

These errors reject the whole call. Errors of single entries are returned in `results`.

| Status | Code | Meaning |
| - | - | - |
| 400 | missing\_parameter | users is missing or empty. |
| 400 | invalid\_parameter | More than 1,000 users. |
| 401 | missing\_api\_key | API key missing. |
| 403 | invalid\_api\_key | API key invalid or not enabled for CabalSpy ID. |
| 429 | rate\_limit\_exceeded | Rate limit exceeded. |

See [Errors](https://docs.cabalspy.xyz/cabalspy-id/errors.md) for the full list.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.