# Rate Limits

Our API employs rate limiting to ensure fair usage and to prevent abuse. Gangmates employs layered rate limiting to protect tenants and the platform. There are **two buckets** evaluated per request:

1. **Endpoint Bucket (per token + IP + route)**
   * Default: **100 requests/min**
   * **Sensitive endpoints:** **10 requests/min** (listed below)
2. **Global Bucket (per company + IP)**
   * **Standard:** 50 requests/min
   * **Premium:** 1,000 requests/min
   * **Enterprise:** 5,000 requests/min

> The **effective limit** is the stricter of the two. For example, a Standard plan calling a non‑sensitive endpoint is limited to **50/min** globally, even though the endpoint bucket allows 100/min.

### Response headers

Every response includes rate‑limit headers so you can programmatically back off:

```
X-RateLimit-Limit-Endpoint: <int>
X-RateLimit-Remaining-Endpoint: <int>
X-RateLimit-Limit-Global: <int>
X-RateLimit-Remaining-Global: <int>
```

On `429 Too Many Requests`, you’ll also receive:

`Retry-After: <seconds>`

### 429 handling (pseudo‑code)

`if response.status == 429:wait(response.headers['Retry-After'] seconds)retry`

> Add jitter/exponential backoff in production clients.

### Errors

Standard HTTP status codes are used.

* `200 OK` – Success
* `201 Created` – Resource created
* `400 Bad Request` – Invalid request
* `401 Unauthorized` – Missing/invalid/expired token
* `403 Forbidden` – Token scopes do not allow this route
* `404 Not Found` – Resource not found
* `429 Too Many Requests` – Rate limit exceeded (see `Retry-After`)

Error bodies are JSON. Examples:

`{ "error": "Unauthorized" }`

### Examples

Here are examples of how you can implement this retry  logic in different programming languages:

{% tabs %}
{% tab title="JavaScript (Axios)" %}
{% code lineNumbers="true" %}

```javascript

const axios = require('axios');

async function makeApiRequest(url, options) {
    try {
        const response = await axios(url, options);
        return response.data;
    } catch (error) {
        if (error.response && error.response.status === 429) {
            // Extract the Retry-After header value
            const retryAfter = parseInt(error.response.headers['retry-after'], 10);

            if (!isNaN(retryAfter)) {
                console.log(`Rate limit exceeded. Retrying after ${retryAfter} seconds...`);
                await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
                return makeApiRequest(url, options); // Retry the request
            } else {
                console.error('Rate limit exceeded but no Retry-After header found.');
            }
        } else {
            console.error('Request failed:', error.message);
        }
    }
}

// Example usage
const url = 'https://api.example.com/your-endpoint';
const options = {
    method: 'GET',
    headers: {
        'Authorization': 'Bearer your_token_here'
    }
};

makeApiRequest(url, options)
    .then(data => {
        console.log('API Response:', data);
    })
    .catch(error => {
        console.error('Request failed:', error.message);
    });
            
```

{% endcode %}

{% endtab %}

{% tab title="Python (Requests)" %}

```python

import time
import requests

def make_api_request(url, headers):
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as err:
        if err.response.status_code == 429:
            retry_after = int(err.response.headers.get('Retry-After', 0))
            if retry_after > 0:
                print(f'Rate limit exceeded. Retrying after {retry_after} seconds...')
                time.sleep(retry_after)
                return make_api_request(url, headers)
            else:
                print('Rate limit exceeded but no Retry-After header found.')
        else:
            print(f'Request failed: {err}')

# Example usage
url = 'https://api.example.com/your-endpoint'
headers = {
    'Authorization': 'Bearer your_token_here'
}

response_data = make_api_request(url, headers)
if response_data:
    print('API Response:', response_data)
            
```

{% endtab %}

{% tab title="Java (HttpClient)" %}

```java

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class ApiClient {
    private static final HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();

    public static String makeApiRequest(String url, String token) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI(url))
                .header("Authorization", "Bearer " + token)
                .GET()
                .build();

        HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() == 429) {
            String retryAfterHeader = response.headers().firstValue("Retry-After").orElse("0");
            int retryAfter = Integer.parseInt(retryAfterHeader);

            if (retryAfter > 0) {
                System.out.println("Rate limit exceeded. Retrying after " + retryAfter + " seconds...");
                Thread.sleep(retryAfter * 1000);
                return makeApiRequest(url, token);
            } else {
                System.out.println("Rate limit exceeded but no Retry-After header found.");
            }
        }

        return response.body();
    }

    public static void main(String[] args) {
        String url = "https://api.example.com/your-endpoint";
        String token = "your_token_here";

        try {
            String response = makeApiRequest(url, token);
            System.out.println("API Response: " + response);
        } catch (Exception e) {
            System.err.println("Request failed: " + e.getMessage());
        }
    }
}
            
```

{% endtab %}
{% endtabs %}

By implementing the above retry mechanism, your application can handle rate limiting gracefully, ensuring that your requests are processed once the rate limit resets. If you have any questions or need further assistance, please contact our support team at <support@gangmates.com>.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.gangmates.com/api-documentation/api-overview/rate-limits.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
