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

# Rate Limits

> Understand and work within Deel API rate limits

## Overview

Deel enforces rate limits to ensure API stability and fair usage across all integrations. Understanding and respecting these limits is essential for building reliable applications.

* Each organization can make up to **5 API requests per second** across all tokens.
* The rate limit is **shared organization-wide**, regardless of how many API tokens you use.
* If you exceed your rate limit, the API will return a **429 Too Many Requests** error.
* Rate limiting uses a **rolling 1-second window** that automatically resets each second.

## Rate Limit Details

### Current limits

| Metric                  | Limit                 |
| ----------------------- | --------------------- |
| **Requests per second** | 5                     |
| **Scope**               | Per organization      |
| **Window**              | 1 second (rolling)    |
| **Error code**          | 429 Too Many Requests |

> **Warning**
>
> Rate limits are enforced **per organization**, not per token. All API tokens within your organization share the same rate limit quota. If you have multiple services or processes making API calls, they all count toward the same 5 requests/second limit.

### How it works

The rate limit operates on a **rolling 1-second window**:

```
Second 1: ✅ ✅ ✅ ✅ ✅ (5 requests - OK)
Second 2: ❌ (6th request - rate limited!)

// Wait for next cycle
Second 3: ✅ ✅ ✅ ✅ ✅ (5 requests - OK again)
```

When you exceed 5 requests per second:

* Additional requests receive a `429` status code
* You must wait until the next cycle starts
* The limit automatically resets each second

## Handling Rate Limit Errors

> **Note**
>
> Deel API does not return rate limit headers. You won't know you've hit the rate limit until you receive a `429` error. This makes proactive rate limiting especially important.

When you exceed the rate limit, you'll receive a `429` error:

```json
{
  "error": "Rate limit exceeded",
  "status": 429,
  "message": "Too many requests. Please wait before retrying."
}
```

### Best practice: Exponential backoff

Implement exponential backoff when hitting rate limits:

**`Node.js`**

```javascript Node.js
async function makeRequestWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      // Success
      if (response.ok) {
        return await response.json();
      }

      // Rate limited
      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After') || 1;
        const delay = Math.min(Math.pow(2, attempt) * 1000, 5000); // Max 5s

        console.log(`Rate limited. Waiting ${delay}ms before retry...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      // Other errors
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);

    } catch (error) {
      if (attempt === maxRetries - 1) throw error;

      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

**`Python`**

```python Python
import time
import requests

def make_request_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.get(url, headers=headers)

            # Success
            if response.ok:
                return response.json()

            # Rate limited
            if response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 1))
                delay = min(2 ** attempt, 5)  # Max 5 seconds

                print(f'Rate limited. Waiting {delay}s before retry...')
                time.sleep(delay)
                continue

            # Other errors
            response.raise_for_status()

        except requests.exceptions.RequestException as e:
            if attempt == max_retries - 1:
                raise

            delay = 2 ** attempt
            time.sleep(delay)
```

## Strategies to Stay Within Limits

#### 1. Request Queuing (Most Important)

Queue requests to ensure you never exceed 5 requests per second:

**`Node.js`**

```javascript Node.js
class RateLimitedQueue {
  constructor(requestsPerSecond = 5) {
    this.queue = [];
    this.interval = 1000 / requestsPerSecond; // 200ms between requests
    this.processing = false;
    this.lastRequestTime = 0;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      if (!this.processing) this.process();
    });
  }

  async process() {
    this.processing = true;

    while (this.queue.length > 0) {
      const { requestFn, resolve, reject } = this.queue.shift();

      // Ensure minimum interval between requests
      const now = Date.now();
      const timeSinceLastRequest = now - this.lastRequestTime;
      if (timeSinceLastRequest < this.interval) {
        await new Promise(r => setTimeout(r, this.interval - timeSinceLastRequest));
      }

      try {
        const result = await requestFn();
        resolve(result);
      } catch (error) {
        reject(error);
      }

      this.lastRequestTime = Date.now();
    }

    this.processing = false;
  }
}

// Usage
const queue = new RateLimitedQueue(5); // 5 requests per second

// Queue multiple requests
const results = await Promise.all([
  queue.add(() => deelAPI.get('/contracts/1')),
  queue.add(() => deelAPI.get('/contracts/2')),
  queue.add(() => deelAPI.get('/contracts/3')),
  // ... up to hundreds of requests
]);
```

**`Python`**

```python Python
import time
import asyncio
from collections import deque

class RateLimitedQueue:
    def __init__(self, requests_per_second=5):
        self.queue = deque()
        self.interval = 1.0 / requests_per_second
        self.processing = False
        self.last_request_time = 0

    async def add(self, request_fn):
        future = asyncio.Future()
        self.queue.append((request_fn, future))

        if not self.processing:
            asyncio.create_task(self.process())

        return await future

    async def process(self):
        self.processing = True

        while self.queue:
            request_fn, future = self.queue.popleft()

            # Ensure minimum interval between requests
            now = time.time()
            time_since_last_request = now - self.last_request_time
            if time_since_last_request < self.interval:
                await asyncio.sleep(self.interval - time_since_last_request)

            try:
                result = await request_fn()
                future.set_result(result)
            except Exception as e:
                future.set_exception(e)

            self.last_request_time = time.time()

        self.processing = False

# Usage
queue = RateLimitedQueue(5)

results = await asyncio.gather(
    queue.add(lambda: get_contract(1)),
    queue.add(lambda: get_contract(2)),
    queue.add(lambda: get_contract(3)),
)
```

#### 2. Batch Requests

When possible, use batch operations instead of individual requests:

```javascript
// ❌ Inefficient - 100 individual requests
for (const contractId of contractIds) {
  await getContract(contractId);
}

// ✅ Efficient - Single batch request (if supported)
const contracts = await getContracts({ ids: contractIds });
```

#### 3. Space Out Requests

**Avoid bursts** of requests. Spread them out over time:

```javascript
// ❌ Bad - Burst of 20 requests at once
await Promise.all(
  contractIds.map(id => getContract(id))
);

// ✅ Good - Controlled rate
for (const id of contractIds) {
  await getContract(id);
  await sleep(200); // 200ms between requests = 5/second
}
```

#### 4. Cache Responses

Reduce API calls by caching frequently accessed data:

```javascript
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300 }); // 5 minute TTL

async function getCachedContract(contractId) {
  // Check cache first
  const cached = cache.get(`contract_${contractId}`);
  if (cached) {
    console.log('Cache hit');
    return cached;
  }

  // Fetch from API if not cached
  const contract = await deelAPI.get(`/contracts/${contractId}`);

  // Store in cache
  cache.set(`contract_${contractId}`, contract);

  return contract;
}
```

#### 5. Centralize API Requests

Since rate limits are organization-wide, centralize request handling to avoid conflicts:

```javascript
class CentralizedAPIClient {
  constructor() {
    this.queue = new RateLimitedQueue(5);
  }

  async makeRequest(method, endpoint, data = null) {
    return this.queue.add(async () => {
      const options = {
        method,
        headers: {
          'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
          'Content-Type': 'application/json'
        }
      };

      if (data) {
        options.body = JSON.stringify(data);
      }

      const response = await fetch(
        `https://api.letsdeel.com/rest${endpoint}`,
        options
      );

      return response.json();
    });
  }

  // Convenience methods
  get(endpoint) { return this.makeRequest('GET', endpoint); }
  post(endpoint, data) { return this.makeRequest('POST', endpoint, data); }
  patch(endpoint, data) { return this.makeRequest('PATCH', endpoint, data); }
}

// Single instance shared across your application
const deelClient = new CentralizedAPIClient();

// All parts of your app use the same client
const contracts = await deelClient.get('/contracts');
const newContract = await deelClient.post('/contracts', contractData);
```

> **Note**
>
> **Important**: Since rate limits are per organization, using multiple tokens won't increase your rate limit. Instead, coordinate all API requests through a centralized queue.

## Best Practices Summary

#### Do's

✅ **Always** implement request queuing

✅ Use exponential backoff for 429 retries

✅ Cache frequently accessed data

✅ Space out requests (avoid bursts)

✅ Set up alerts for rate limit errors

✅ Monitor 429 error frequency

✅ Centralize API requests across your organization

#### Don'ts

❌ Don't send bursts of requests

❌ Don't ignore 429 errors

❌ Don't retry immediately without delay

❌ Don't make unnecessary API calls

❌ Don't use tight polling loops

❌ Don't assume you know remaining quota (no headers provided)

❌ Don't run multiple uncoordinated processes making API calls

## Troubleshooting

#### Frequently hitting rate limits

**Solutions:**

1. Implement request queuing (most important!)
2. Add caching layer
3. Batch operations where possible
4. Review if all requests are necessary
5. Space out requests more (reduce from 5/sec to 4/sec for safety margin)
6. Identify and optimize high-volume operations

#### Unpredictable rate limiting

**Possible causes:**

* Multiple services/processes making requests simultaneously
* Background jobs running concurrently
* Different parts of your application not coordinating requests
* Rate limit shared across entire organization

**Solutions:**

* Centralize all API requests through a single queue
* Coordinate between services (use Redis or similar for distributed rate limiting)
* Monitor which services are making requests
* Implement request prioritization

#### Rate limits affecting production

**Immediate actions:**

1. Enable request queuing immediately
2. Increase delays between requests (use 4/sec instead of 5/sec)
3. Implement caching for frequently accessed data
4. Identify services making excessive requests
5. Contact Deel support if you need higher limits

## Next Steps

#### [Idempotency](/api/idempotency)

Learn about safe request retries

#### [Best Practices](/api/best-practices)

Comprehensive integration best practices

#### [Error Handling](/api/best-practices#error-handling--retries)

Implement robust error handling

#### [Webhooks](/api/webhooks/quickstart)

Reduce polling with webhooks