---
title: "Migrate API key(s)"
method: POST
path: "/v2/keys.migrateKeys"
tags: ["keys"]
---

# Migrate API key(s)

`POST /v2/keys.migrateKeys`

Returns HTTP 200 even on partial success; hashes that could not be migrated are listed under `data.failed`.

**Required Permissions**
Your root key must have one of the following permissions for basic key information:
- `api.*.create_key` (to migrate keys to any API)
- `api.<api_id>.create_key` (to migrate keys to a specific API)

## Request body

- V2KeysMigrateKeysRequestBody
  - `migrationId` string, required — Identifier of the configured migration provider/strategy to use (e.g., "your_company"). You will receive this from Unkey's support staff.
  - `apiId` string, required — The ID of the API that the keys should be inserted into
  - `keys` V2KeysMigrateKeyData[], required
    - `hash` string, required — The current hash of the key on your side
    - `name` string — Sets a human-readable identifier for internal organization and dashboard display. Never exposed to end users, only visible in management interfaces and API responses. Avoid generic names like "API Key" when managing multiple keys for the same user or service.
    - `externalId` string — Links this key to a user or entity in your system using your own identifier. Returned during verification to identify the key owner without additional database lookups. Essential for user-specific analytics, billing, and multi-tenant key management. Use your primary user ID, organization ID, or tenant ID for best results. Accepts letters, numbers, underscores, dots, and hyphens for flexible identifier formats.
    - `meta` object — Stores arbitrary JSON metadata returned during key verification for contextual information. Eliminates additional database lookups during verification, improving performance for stateless services. Avoid storing sensitive data here as it's returned in verification responses. Large metadata objects increase verification latency and should stay under 10KB total size.
    - `roles` string[] — Assigns existing roles to this key for permission management through role-based access control. Roles must already exist in your workspace before assignment. During verification, all permissions from assigned roles are checked against requested permissions. Roles provide a convenient way to group permissions and apply consistent access patterns across multiple keys.
    - `permissions` string[] — Grants specific permissions directly to this key without requiring role membership. Wildcard permissions like `documents.*` grant access to all sub-permissions including `documents.read` and `documents.write`. Direct permissions supplement any permissions inherited from assigned roles.
    - `expires` integer — Sets when this key automatically expires as a Unix timestamp in milliseconds. Verification fails with code=EXPIRED immediately after this time passes. Omitting this field creates a permanent key that never expires. Avoid setting timestamps in the past as they immediately invalidate the key. Keys expire based on server time, not client time, which prevents timezone-related issues. Essential for trial periods, temporary access, and security compliance requiring key rotation.
    - `enabled` boolean — Controls whether the key is active immediately upon creation. When set to `false`, the key exists but all verification attempts fail with `code=DISABLED`. Useful for pre-creating keys that will be activated later or for keys requiring manual approval. Most keys should be created with `enabled=true` for immediate use.
    - `credits` KeyCreditsData — Credit configuration and remaining balance for this key.
      - `remaining` integer, nullable, required — Number of credits remaining (null for unlimited).
      - `refill` KeyCreditsRefill — Configuration for automatic credit refill behavior.
        - `interval` 'daily' | 'monthly', required — How often credits are automatically refilled.
        - `amount` integer, required — Number of credits to add during each refill cycle.
        - `refillDay` integer — Day of the month for monthly refills (1-31). Only required when interval is 'monthly'. For days beyond the month's length, refill occurs on the last day of the month.
    - `ratelimits` RatelimitRequest[] — Defines time-based rate limits that protect against abuse by controlling request frequency. Unlike credits which track total usage, rate limits reset automatically after each window expires. Multiple rate limits can control different operation types with separate thresholds and windows. Essential for preventing API abuse while maintaining good performance for legitimate usage.
      - `name` string, required — The name of this rate limit. This name is used to identify which limit to check during key verification. Best practices for limit names: - Use descriptive, semantic names like 'api_requests', 'heavy_operations', or 'downloads' - Be consistent with naming conventions across your application - Create separate limits for different resource types or operation costs - Consider using namespaced names for better organization (e.g., 'files.downloads', 'compute.training') You will reference this exact name when verifying keys to check against this specific limit.
      - `limit` integer, required — The maximum number of operations allowed within the specified time window. When this limit is reached, verification requests will fail with `code=RATE_LIMITED` until the window resets. The limit should reflect: - Your infrastructure capacity and scaling limitations - Fair usage expectations for your service - Different tier levels for various user types - The relative cost of the operations being limited Higher values allow more frequent access but may impact service performance.
      - `duration` integer, required — The duration for each ratelimit window in milliseconds. This controls how long the rate limit counter accumulates before resetting. Common values include: - 1000 (1 second): For strict per-second limits on high-frequency operations - 60000 (1 minute): For moderate API usage control - 3600000 (1 hour): For less frequent but costly operations - 86400000 (24 hours): For daily quotas Shorter windows provide more frequent resets but may allow large burst usage. Longer windows provide more consistent usage patterns but take longer to reset after limit exhaustion.
      - `autoApply` boolean, required — Whether this ratelimit should be automatically applied when verifying a key.

## Response `200`

Successfully migrated keys.

- V2KeysMigrateKeysResponseBody
  - `meta` Meta, required — Metadata object included in every API response. This provides context about the request and is essential for debugging, audit trails, and support inquiries. The `requestId` is particularly important when troubleshooting issues with the Unkey support team.
    - `requestId` string, required — A unique id for this request. Always include this ID when contacting support about a specific API request. This identifier allows Unkey's support team to trace the exact request through logs and diagnostic systems to provide faster assistance.
  - `data` V2KeysMigrateKeysResponseData, required
    - `migrated` V2KeysMigrateKeysMigration[], required — Successfully migrated keys with their hash and generated keyId
      - `hash` string, required — The hash provided in the migration request
      - `keyId` string, required — The unique identifier for this key in Unkey's system. This is NOT the actual API key, but a reference ID used for management operations like updating or deleting the key. Store this ID in your database to reference the key later. This ID is not sensitive and can be logged or displayed in dashboards.
    - `failed` string[], required — Hashes that could not be migrated (e.g., already exist in the system)

## Other responses

- `400` — Bad request
- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not found
- `429` — Too Many Requests
- `500` — Internal server error

---

[API](https://skmtc.net/unkeyed/apis/unkey-api.md) · [All operations](https://skmtc.net/unkeyed/apis/unkey-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/unkeyed/unkey-api/revisions/4bd11a7252bc/schema)
