GET /rate-limit
Always returns 429 Too Many Requests with Retry-After and X-RateLimit-* headers. Use ?lie=true to send a deceptive Retry-After value — the response body reports the real requested value so you can verify whether your client respected the header.
retry_after
Retry-After header value in seconds. Range: 1–3600. Default: 60.
lie
If true, Retry-After is set to 1 regardless of retry_after. The body still reports the requested value. Default: false.
# Honest 429 — wait 30 seconds before retrying
curl -i 'https://chaos.catastrophic.io/rate-limit?retry_after=30'
# Lying 429 — header says 1s, body says 30s
curl -i 'https://chaos.catastrophic.io/rate-limit?retry_after=30&lie=true'
import urllib.request, urllib.error
try:
urllib.request.urlopen("https://chaos.catastrophic.io/rate-limit?retry_after=30")
except urllib.error.HTTPError as e:
print(e.code) # 429
print("Retry-After:", e.headers["Retry-After"])
print("Remaining:", e.headers["X-RateLimit-Remaining"])
print("Lied:", e.headers.get("X-Chaos-Rate-Limit-Lied"))
const res = await fetch("https://chaos.catastrophic.io/rate-limit?retry_after=30");
console.log(res.status); // 429
console.log("Retry-After:", res.headers.get("retry-after"));
console.log("Remaining:", res.headers.get("x-ratelimit-remaining"));
console.log("Lied:", res.headers.get("x-chaos-rate-limit-lied"));
package main
import (
"fmt"
"net/http"
)
func main() {
resp, _ := http.Get("https://chaos.catastrophic.io/rate-limit?retry_after=30")
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
fmt.Println("Retry-After:", resp.Header.Get("Retry-After"))
fmt.Println("Remaining:", resp.Header.Get("X-RateLimit-Remaining"))
fmt.Println("Lied:", resp.Header.Get("X-Chaos-Rate-Limit-Lied"))
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }
fn main() -> Result<(), Box> {
let resp = reqwest::blocking::get("https://chaos.catastrophic.io/rate-limit?retry_after=30")?;
let h = resp.headers();
println!("{}", resp.status());
println!("Retry-After: {:?}", h.get("retry-after"));
println!("Remaining: {:?}", h.get("x-ratelimit-remaining"));
println!("Lied: {:?}", h.get("x-chaos-rate-limit-lied"));
Ok(())
}
import java.net.URI;
import java.net.http.*;
public class RateLimit {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("https://chaos.catastrophic.io/rate-limit?retry_after=30")).build();
var resp = client.send(req, HttpResponse.BodyHandlers.discarding());
var h = resp.headers();
System.out.println(resp.statusCode());
System.out.println("Retry-After: " + h.firstValue("Retry-After").orElse(""));
System.out.println("Remaining: " + h.firstValue("X-RateLimit-Remaining").orElse(""));
System.out.println("Lied: " + h.firstValue("X-Chaos-Rate-Limit-Lied").orElse(""));
}
}
using var client = new HttpClient();
var resp = await client.GetAsync("https://chaos.catastrophic.io/rate-limit?retry_after=30");
Console.WriteLine((int)resp.StatusCode);
Console.WriteLine($"Retry-After: {resp.Headers.RetryAfter}");
resp.Headers.TryGetValues("X-RateLimit-Remaining", out var rem);
resp.Headers.TryGetValues("X-Chaos-Rate-Limit-Lied", out var lied);
Console.WriteLine($"Remaining: {rem?.FirstOrDefault()}");
Console.WriteLine($"Lied: {lied?.FirstOrDefault()}");
require "net/http"
res = Net::HTTP.get_response(URI("https://chaos.catastrophic.io/rate-limit?retry_after=30"))
puts res.code # 429
puts "Retry-After: #{res["Retry-After"]}"
puts "Remaining: #{res["X-RateLimit-Remaining"]}"
puts "Lied: #{res["X-Chaos-Rate-Limit-Lied"]}"
# PowerShell 5.1: Invoke-WebRequest throws on 429.
try {
Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/rate-limit?retry_after=30'
} catch {
$resp = $_.Exception.Response
$resp.StatusCode.value__ # 429
$resp.Headers['Retry-After'] # seconds to wait
$resp.Headers['X-RateLimit-Remaining'] # 0
}