Skip to main content
The Everhour API enforces rate limits to ensure stability for all users.

Current limits

The limit is 100 requests per 10 seconds per API key. This value may vary with server load and is subject to change.

Exceeded limit response

When you exceed the rate limit, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 10
The Retry-After header specifies how many seconds to wait before retrying.

Handling 429 responses

Implement exponential backoff in your integration:
for attempt in 1 2 3 4 5; do
  response=$(curl -s -w "\n%{http_code}" https://api.everhour.com/projects \
    -H "X-Api-Key: YOUR_API_KEY")
  status=$(echo "$response" | tail -1)
  if [ "$status" != "429" ]; then break; fi
  sleep $(( 2 ** attempt ))
done
require "net/http"

def request_with_retry(uri, headers, max_retries: 5)
  max_retries.times do |attempt|
    req = Net::HTTP::Get.new(uri, headers)
    resp = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    return resp unless resp.code == "429"
    sleep(resp["Retry-After"]&.to_i || 2**attempt)
  end
  raise "Max retries exceeded"
end
import time
import requests

def request_with_retry(url, headers, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        if response.status_code == 429:
            wait = int(response.headers.get("Retry-After", 2 ** attempt))
            time.sleep(wait)
            continue
        return response
    raise Exception("Max retries exceeded")
function request_with_retry(string $url, array $headers, int $max_retries = 5): string {
    for ($attempt = 0; $attempt < $max_retries; $attempt++) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $body = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($status !== 429) return $body;
        sleep(pow(2, $attempt));
    }
    throw new RuntimeException("Max retries exceeded");
}
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

HttpClient client = HttpClient.newHttpClient();

HttpResponse<String> requestWithRetry(String url, int maxRetries) throws Exception {
    for (int attempt = 0; attempt < maxRetries; attempt++) {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("X-Api-Key", "YOUR_API_KEY")
            .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() != 429) return response;
        long wait = response.headers().firstValue("Retry-After")
            .map(Long::parseLong).orElse((long) Math.pow(2, attempt));
        Thread.sleep(wait * 1000);
    }
    throw new RuntimeException("Max retries exceeded");
}
async function requestWithRetry(url, headers, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });
    if (response.status !== 429) return response;
    const wait = parseInt(response.headers.get("Retry-After") ?? String(2 ** attempt), 10);
    await new Promise(resolve => setTimeout(resolve, wait * 1000));
  }
  throw new Error("Max retries exceeded");
}
// import "fmt"; "math"; "net/http"; "time"
func requestWithRetry(url string, apiKey string, maxRetries int) (*http.Response, error) {
    for attempt := 0; attempt < maxRetries; attempt++ {
        req, _ := http.NewRequest("GET", url, nil)
        req.Header.Set("X-Api-Key", apiKey)
        resp, err := http.DefaultClient.Do(req)
        if err != nil || resp.StatusCode != 429 {
            return resp, err
        }
        time.Sleep(time.Duration(math.Pow(2, float64(attempt))) * time.Second)
    }
    return nil, fmt.Errorf("max retries exceeded")
}
async Task<HttpResponseMessage> RequestWithRetry(HttpClient client, string url, int maxRetries = 5) {
    for (int attempt = 0; attempt < maxRetries; attempt++) {
        var response = await client.GetAsync(url);
        if (response.StatusCode != System.Net.HttpStatusCode.TooManyRequests) return response;
        var wait = response.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(Math.Pow(2, attempt));
        await Task.Delay(wait);
    }
    throw new Exception("Max retries exceeded");
}

Batch operations

If your use case requires a large volume of requests — such as a one-time data migration or bulk export — contact us before running it. We may be able to provide a more efficient data access method. Reach us via chat inside your Everhour account or at ask@everhour.com.