> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getply.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getply.com/_mcp/server.

# Authentication

# Authentication

The Ply API uses API key authentication. Every request must include your API key in the `API_KEY` header.

```bash
curl -X GET "https://api.getply.com/api/v1/materials" \
  -H "API_KEY: YOUR_API_KEY"
```

> **Warn**
>
> API keys grant access to your business data. Treat them like passwords — never commit them to source control or expose them in client-side code.

## Creating an API key

### Open the Ply dashboard

Go to [app.getply.com](https://app.getply.com) and sign in with an Owner or Admin account.

### Navigate to API settings

Go to **Settings > Workspace > API Keys**.

### Create a new key

Click **Create API Key**, give it a descriptive name (e.g., "ERP Sync" or "Dashboard Integration"), and select the scopes your integration needs.

### Copy your key

Your full API key is shown only once. Copy it and store it securely (e.g., in a secrets manager or environment variable).

## Scopes

API keys are scoped to limit what they can access. When creating a key, select only the scopes your integration needs. A key without the required scope for an endpoint will receive a `403 Insufficient Scope` error.

### Available scopes

| Scope                         | Access                                                              |
| ----------------------------- | ------------------------------------------------------------------- |
| `materials:read`              | List and get materials, variations                                  |
| `materials:write`             | Create, update, delete materials                                    |
| `materials_availability:read` | View material availability across locations                         |
| `kits:read`                   | List and get kits, kit materials                                    |
| `kits:write`                  | Create, update, delete kits                                         |
| `purchase_orders:read`        | List and get purchase orders, line items                            |
| `purchase_orders:write`       | Create, update, delete purchase orders and items                    |
| `suppliers:read`              | List and get suppliers and supplier categories                      |
| `suppliers:write`             | Create, update, delete suppliers and supplier categories            |
| `categories:read`             | List and get material categories and tags                           |
| `categories:write`            | Create, update, delete material categories and tags                 |
| `field_requests:read`         | List and get field requests                                         |
| `field_requests:write`        | Create, update, delete field requests; change status/priority       |
| `pick_lists:read`             | List and get pick lists, pick list materials                        |
| `pick_lists:write`            | Create, update, delete pick lists and materials                     |
| `returns:read`                | List and get returns, return items                                  |
| `returns:write`               | Create, update, delete returns and items                            |
| `rfqs:read`                   | List and get RFQs, RFQ suppliers                                    |
| `rfqs:write`                  | Create, update RFQs; manage suppliers                               |
| `inventory:read`              | View inventory history and snapshots                                |
| `inventory:write`             | Write access for inventory history                                  |
| `inventory_counts:read`       | List and get inventory counts and count items                       |
| `inventory_counts:write`      | Create and update inventory counts                                  |
| `locations:read`              | List and get locations, location materials                          |
| `jobs:read`                   | List and get jobs, job materials, job equipment                     |
| `jobs:write`                  | Create, update, delete jobs; open and close them; add job materials |
| `equipment:read`              | List and get equipment and serialized units                         |
| `users:read`                  | List and get users                                                  |
| `reports:read`                | Access low-stock, dead-shelf, usage, and variance reports           |

> **Info**
>
> Write scopes implicitly include read access for the same resource. A key with `materials:write` can also read materials.

## Rate limits

API requests are rate-limited per business to ensure fair usage across all integrations.

| Limit               | Window     | Description                        |
| ------------------- | ---------- | ---------------------------------- |
| **60 requests**     | Per minute | Burst limit for short time windows |
| **10,000 requests** | Per day    | Daily quota across all endpoints   |

All API keys belonging to the same business share a single rate limit pool. Creating multiple keys does not increase your limits.

### Rate limit headers

Every response includes headers showing your current usage:

```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 45
```

| Header                  | Description                                    |
| ----------------------- | ---------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests remaining before throttling           |
| `X-RateLimit-Reset`     | Seconds until the rate limit window resets     |

### Handling `429 Too Many Requests`

When you exceed the rate limit, the API returns:

```json
{
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded. Please retry after 12 seconds.",
  "status": 429,
  "timestamp": "2026-03-20T14:30:00",
  "path": "/api/v1/materials"
}
```

The `Retry-After` header tells you how many seconds to wait before retrying:

```
Retry-After: 12
```

> **Tip**
>
> Implement exponential backoff in your integration. Start with the `Retry-After` value, then double the wait time on consecutive 429s.

## Environments

| Environment    | Base URL                                | Purpose                             |
| -------------- | --------------------------------------- | ----------------------------------- |
| **Production** | `https://api.getply.com/api/v1`         | Live business data                  |
| **Sandbox**    | `https://sandbox-api.getply.com/api/v1` | Test integrations before going live |

Use separate API keys for each environment. Sandbox keys do not work in production and vice versa.

## Example: Complete authenticated request

```bash
curl -X GET "https://api.getply.com/api/v1/purchase-orders?page=0&size=10&sort=createdAt,desc" \
  -H "API_KEY: YOUR_API_KEY" \
  -H "Accept: application/json"
```

```json
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "orderNumber": "PO-1042",
      "status": "CONFIRMED",
      "supplier": {
        "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "name": "Ferguson Enterprises"
      },
      "location": {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "name": "Main Warehouse"
      },
      "totalCost": 1249.50,
      "createdAt": "2026-03-15T09:22:00",
      "updatedAt": "2026-03-18T14:05:30"
    }
  ],
  "pagination": {
    "page": 0,
    "size": 10,
    "totalElements": 47,
    "totalPages": 5
  }
}
```

## Security best practices

* **Rotate keys regularly** — revoke and recreate keys periodically
* **Use minimum scopes** — only grant the access your integration needs
* **Store keys securely** — use environment variables or a secrets manager, never hardcode
* **Monitor usage** — check the API Keys page in the dashboard for usage metrics
* **Use sandbox for development** — test against sandbox before pointing at production