***

title: Rate Limits
description: 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.
</Warning>

### 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.
</Note>

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:

<CodeGroup>
  ```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
  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)
  ```
</CodeGroup>

## Strategies to Stay Within Limits

<AccordionGroup>
  <Accordion title="1. Request Queuing (Most Important)" icon="list">
    Queue requests to ensure you never exceed 5 requests per second:

    <CodeGroup>
      ```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
      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)),
      )
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="2. Batch Requests" icon="layer-group">
    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 });
    ```
  </Accordion>

  <Accordion title="3. Space Out Requests" icon="clock">
    **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
    }
    ```
  </Accordion>

  <Accordion title="4. Cache Responses" icon="database">
    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;
    }
    ```
  </Accordion>

  <Accordion title="5. Centralize API Requests" icon="sitemap">
    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/v2${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.
    </Note>
  </Accordion>
</AccordionGroup>

## Best Practices Summary

<AccordionGroup>
  <Accordion title="Do's" icon="check">
    ✅ **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
  </Accordion>

  <Accordion title="Don'ts" icon="xmark">
    ❌ 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
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Frequently hitting rate limits" icon="gauge">
    **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
  </Accordion>

  <Accordion title="Unpredictable rate limiting" icon="question">
    **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
  </Accordion>

  <Accordion title="Rate limits affecting production" icon="triangle-exclamation">
    **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
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Idempotency" icon="fa-light repeat" href="/api/idempotency">
    Learn about safe request retries
  </Card>

  <Card title="Best Practices" icon="fa-light book" href="/api/best-practices">
    Comprehensive integration best practices
  </Card>

  <Card title="Error Handling" icon="fa-light triangle-exclamation" href="/api/best-practices#error-handling--retries">
    Implement robust error handling
  </Card>

  <Card title="Webhooks" icon="fa-light webhook" href="/api/webhooks/quickstart">
    Reduce polling with webhooks
  </Card>
</CardGroup>
