{
  "protocol": "fx-api-freshness-benchmark",
  "version": "1.0",
  "published_at": "2026-07-16",
  "canonical_url": "https://exchangerate.dev/learn/fx-api-freshness-benchmark",
  "status": "methodology_published",
  "purpose": "Compare observable exchange-rate API freshness and transparency without treating a single response or marketing claim as a benchmark result.",
  "scope": {
    "pair": "EUR/USD",
    "poll_interval_seconds": 60,
    "minimum_calendar_days": 7,
    "minimum_open_market_days": 5,
    "minimum_successful_observations_per_provider": 500,
    "measurement_region_must_be_reported": true
  },
  "eligibility": [
    "The endpoint is publicly documented or the account holder has permission to measure it.",
    "The plan and authentication state used for the run are disclosed.",
    "The response includes a rate and a provider-reported data timestamp; providers without a data timestamp may be measured for availability and latency but are unscored for data age.",
    "The collection interval complies with the provider's published limits and terms."
  ],
  "recorded_fields": [
    "provider",
    "plan",
    "pair",
    "request_started_at",
    "response_received_at",
    "http_status",
    "rate",
    "provider_data_updated_at",
    "provider_source_label",
    "provider_market_session_label",
    "response_latency_ms",
    "error_class"
  ],
  "metrics": {
    "data_age_seconds": "response_received_at minus provider_data_updated_at",
    "observed_change_interval_seconds": "elapsed time between consecutive changes in the returned numeric rate; this is not asserted to equal upstream ingest cadence",
    "availability_percent": "successful parseable observations divided by scheduled observations",
    "response_latency_ms": "request start to complete response, reported with measurement region",
    "timestamp_coverage_percent": "successful observations containing a parseable provider data timestamp",
    "source_label_coverage_percent": "successful observations containing a source or freshness class",
    "weekend_label_accuracy": "whether an endpoint explicitly distinguishes a closed market or carried observation from a fresh trading-week observation"
  },
  "reporting": {
    "required_statistics": [
      "sample_count",
      "success_count",
      "p50_data_age_seconds",
      "p95_data_age_seconds",
      "p50_observed_change_interval_seconds",
      "p95_observed_change_interval_seconds",
      "availability_percent",
      "timestamp_coverage_percent",
      "source_label_coverage_percent"
    ],
    "required_disclosures": [
      "collection start and end timestamps",
      "measurement region",
      "provider plan",
      "pair direction and precision",
      "parser version",
      "excluded observations and reasons",
      "known clock or network limitations"
    ],
    "prohibited_claims": [
      "Calling a provider fastest from one observation",
      "Treating observed rate changes as proof of upstream ingest frequency",
      "Scoring data age when no provider data timestamp exists",
      "Presenting a weekend-carried value as a fresh weekend quote",
      "Publishing provider results below the minimum evidence threshold"
    ]
  },
  "results": {
    "status": "not_yet_published",
    "providers": [],
    "note": "No provider ranking or measured result is claimed in protocol version 1.0."
  }
}
