# Rate limits

For designers and integrators.

Every authenticated request counts toward 500 per minute per API key (per user for signed-in dashboard sessions). Operations that create quotes, uploads and orders, or move money, also count toward 60 per minute. Public sign-up, catalog and shipping endpoints are limited per IP address. Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`; past a limit the answer is 429 `rate_limited` with a `Retry-After` header.

## The limits

Limits are counted in fixed windows. Each operation lists the limits it counts toward in `x-ratelimit` (`limit` requests per `window_seconds`, `per` API key or user, or per IP address); these are the defaults.

| Applies to | Limit | Counted per |
| --- | --- | --- |
| Every authenticated request | 500 per minute | API key, or user for dashboard sessions |
| Creating quotes, uploads and orders, cancelling orders, top-ups, card setup and withdrawals (on top of the above) | 60 per minute | API key, or user |
| Integrator sign-up (`create_integrator_account`) | 10 per hour | IP address |
| Resending the confirmation email (`resend_integrator_confirmation`) | 5 per hour | IP address |
| Catalog and shipping reads (materials, categories, shipping rates and quotes) | 120 per minute | IP address |

`get_health` and the OpenAPI document are not limited.

Every limited response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix seconds) for the limit closest to being reached. Past a limit the API answers 429 with code `rate_limited`, a `Retry-After` header in seconds, and `details` with `limit`, `window_seconds`, `reset_at` and `retry_after_seconds`. A refused request does not count. A request replayed with the same `Idempotency-Key` counts like any other request.

## Quotes, uploads, orders and payments

These operations start slicing, create files or move money, so they also count toward a tighter limit per API key (or signed-in user). Each request still counts toward the general limit too.

-   [POST Quote a model file `/v2/quotes`](https://public-api.dapi.digital/docs/reference/quotes.md#create_quote)
-   [POST Create an order `/v2/orders`](https://public-api.dapi.digital/docs/reference/orders.md#create_order)
-   [POST Cancel an order `/v2/orders/{id}/cancel`](https://public-api.dapi.digital/docs/reference/orders.md#cancel_order)
-   [POST Order prints of your quotes `/v2/integrator/orders`](https://public-api.dapi.digital/docs/reference/integrator-orders.md#create_integrator_order)
-   [POST Cancel an order `/v2/integrator/orders/{id}/cancel`](https://public-api.dapi.digital/docs/reference/integrator-orders.md#cancel_integrator_order)
-   [POST Buy stock batches `/v2/stock-purchases`](https://public-api.dapi.digital/docs/reference/stocks.md#create_stock_purchase)
-   [POST Start adding a card `/v2/cards/setup-sessions`](https://public-api.dapi.digital/docs/reference/wallet.md#start_card_setup)
-   [POST Top up your wallet `/v2/top-ups`](https://public-api.dapi.digital/docs/reference/wallet.md#start_top_up)
-   [POST Request a withdrawal `/v2/withdrawals`](https://public-api.dapi.digital/docs/reference/wallet.md#create_withdrawal)
-   [POST Start a file upload `/v2/uploads`](https://public-api.dapi.digital/docs/reference/uploads.md#create_upload)

## Public endpoints

Endpoints that need no key are limited per IP address:

-   [POST Sign up as an integrator `10 per hour`](https://public-api.dapi.digital/docs/reference/integrator-onboarding.md#create_integrator_account)
-   [POST Resend the confirmation email `5 per hour`](https://public-api.dapi.digital/docs/reference/integrator-onboarding.md#resend_integrator_confirmation)
-   [GET Get the shipping rate card `120 per minute`](https://public-api.dapi.digital/docs/reference/shipping.md#get_shipping_rates)
-   [GET Get a shipping price `120 per minute`](https://public-api.dapi.digital/docs/reference/shipping.md#get_shipping_quote)
-   [GET List catalog materials `120 per minute`](https://public-api.dapi.digital/docs/reference/materials.md#list_materials)
-   [GET Get a catalog material `120 per minute`](https://public-api.dapi.digital/docs/reference/materials.md#get_material)
-   [GET List product categories `120 per minute`](https://public-api.dapi.digital/docs/reference/categories.md#list_categories)
-   [GET Get a product category `120 per minute`](https://public-api.dapi.digital/docs/reference/categories.md#get_category)

## Handling a 429

Watch `X-RateLimit-Remaining` and slow down before it reaches 0. When a request is refused, wait for the number of seconds in `Retry-After`, then send it again. Retry a write with the same `Idempotency-Key`, so it never runs twice.

**Retry after a 429 (Node.js)**

```javascript
async function callWithRetry(url, init, attempts = 5) {
  for (let attempt = 1; ; attempt++) {
    const res = await fetch(url, init);
    if (res.status !== 429 || attempt === attempts) return res;
    // Retry-After is in seconds; wait at least that long.
    const seconds = Number(res.headers.get('retry-after') ?? 1);
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
  }
}
```

**Retry after a 429 (Python)**

```python
import time
import requests

def call_with_retry(method, url, attempts=5, **kwargs):
    for attempt in range(1, attempts + 1):
        res = requests.request(method, url, **kwargs)
        if res.status_code != 429 or attempt == attempts:
            return res
        # Retry-After is in seconds; wait at least that long.
        time.sleep(int(res.headers.get("Retry-After", "1")))
```

**A 429 response body**

```json
{
  "status_code": 429,
  "error": "Too Many Requests",
  "code": "rate_limited",
  "message": "Too many requests with this API key: the limit is 500 per minute. Retry after 42 seconds (at 2026-10-22T10:01:00.000Z).",
  "details": {
    "limit": 500,
    "window_seconds": 60,
    "reset_at": "2026-10-22T10:01:00.000Z",
    "retry_after_seconds": 42,
    "policy": "per_credential",
    "request_id": "req_6Yb2kQe0VxLr4TnPz8sWa1Mc"
  }
}
```

---

3DAPI API Docs. This page on the web: https://public-api.dapi.digital/docs/guides/rate-limits. Index of every page: https://public-api.dapi.digital/docs/llms.txt.
