{
  "license": "CC BY 4.0",
  "recipe": {
    "slug": "api-rate-limit-recovery",
    "title": "Measure API 429 Rate-Limit Recovery",
    "testedWith": "Apache DataFusion 45.2.0",
    "lastReviewed": "2026-07-28"
  },
  "contract": {
    "tableName": "api_attempt_events",
    "schema": [
      {
        "name": "timestamp_utc",
        "type": "Timestamp",
        "description": "Attempt completion time in UTC."
      },
      {
        "name": "route_template",
        "type": "Utf8",
        "description": "Stable route template."
      },
      {
        "name": "request_id",
        "type": "Utf8",
        "description": "Identifier shared by the original attempt and retries."
      },
      {
        "name": "attempt_number",
        "type": "Int64",
        "description": "One-based attempt sequence."
      },
      {
        "name": "status_code",
        "type": "Int64",
        "description": "HTTP status returned for the attempt."
      },
      {
        "name": "retry_after_ms",
        "type": "Int64",
        "description": "Applied retry delay in milliseconds."
      },
      {
        "name": "environment",
        "type": "Utf8",
        "description": "Deployment environment."
      }
    ]
  },
  "query": "WITH request_outcomes AS (\n  SELECT\n    route_template,\n    request_id,\n    COUNT(*) AS attempts,\n    MAX(CASE WHEN status_code = 429 THEN 1 ELSE 0 END) AS was_rate_limited,\n    MAX(CASE WHEN status_code BETWEEN 200 AND 299 THEN 1 ELSE 0 END) AS succeeded\n  FROM api_attempt_events\n  WHERE timestamp_utc >= now() - INTERVAL '24 hours'\n    AND environment = 'production'\n  GROUP BY route_template, request_id\n)\nSELECT\n  route_template,\n  COUNT(*) AS requests,\n  SUM(was_rate_limited) AS rate_limited_requests,\n  SUM(CASE\n    WHEN was_rate_limited = 1 AND succeeded = 1 THEN 1\n    ELSE 0\n  END) AS recovered_requests,\n  100.0 * SUM(CASE\n    WHEN was_rate_limited = 1 AND succeeded = 1 THEN 1\n    ELSE 0\n  END) / NULLIF(SUM(was_rate_limited), 0) AS recovery_rate_pct\nFROM request_outcomes\nGROUP BY route_template\nORDER BY rate_limited_requests DESC, route_template;",
  "fixtureKind": "reproducible",
  "inputRows": [
    {
      "timestamp_utc": "2026-07-28T17:59:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-1",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:58:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-1",
      "attempt_number": 2,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:57:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-2",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:56:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-2",
      "attempt_number": 2,
      "status_code": 429,
      "retry_after_ms": 2000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:55:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-2",
      "attempt_number": 3,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:54:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-3",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:53:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-3",
      "attempt_number": 2,
      "status_code": 429,
      "retry_after_ms": 2000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:52:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-4",
      "attempt_number": 1,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:51:00.000Z",
      "route_template": "/api/export",
      "request_id": "export-1",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:50:00.000Z",
      "route_template": "/api/export",
      "request_id": "export-1",
      "attempt_number": 2,
      "status_code": 429,
      "retry_after_ms": 2000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:49:00.000Z",
      "route_template": "/api/export",
      "request_id": "export-2",
      "attempt_number": 1,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    }
  ],
  "illustrativeInputRows": [
    {
      "timestamp_utc": "2026-07-28T17:59:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-1",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:58:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-1",
      "attempt_number": 2,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:57:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-2",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:56:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-2",
      "attempt_number": 2,
      "status_code": 429,
      "retry_after_ms": 2000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:55:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-2",
      "attempt_number": 3,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:54:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-3",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:53:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-3",
      "attempt_number": 2,
      "status_code": 429,
      "retry_after_ms": 2000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:52:00.000Z",
      "route_template": "/api/search",
      "request_id": "search-4",
      "attempt_number": 1,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:51:00.000Z",
      "route_template": "/api/export",
      "request_id": "export-1",
      "attempt_number": 1,
      "status_code": 429,
      "retry_after_ms": 1000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:50:00.000Z",
      "route_template": "/api/export",
      "request_id": "export-1",
      "attempt_number": 2,
      "status_code": 429,
      "retry_after_ms": 2000,
      "environment": "production"
    },
    {
      "timestamp_utc": "2026-07-28T17:49:00.000Z",
      "route_template": "/api/export",
      "request_id": "export-2",
      "attempt_number": 1,
      "status_code": 200,
      "retry_after_ms": 0,
      "environment": "production"
    }
  ],
  "expectedOutput": {
    "columns": [
      "route_template",
      "requests",
      "rate_limited_requests",
      "recovered_requests",
      "recovery_rate_pct"
    ],
    "rows": [
      {
        "route_template": "/api/search",
        "requests": 4,
        "rate_limited_requests": 3,
        "recovered_requests": 2,
        "recovery_rate_pct": 66.66666667
      },
      {
        "route_template": "/api/export",
        "requests": 2,
        "rate_limited_requests": 1,
        "recovered_requests": 0,
        "recovery_rate_pct": 0
      }
    ]
  },
  "notes": [
    "The included synthetic input rows generate the expected output when timestamps are shifted into the documented query window.",
    "Run this fixture in the public browser SQL playground; the selected query executes locally with DuckDB-Wasm.",
    "Adapt time windows, thresholds, identities, and business definitions before operational use."
  ]
}