{
  "openapi": "3.1.0",
  "info": {
    "title": "Modan African Currency Data API",
    "description": "African FX and provider pricing data — hourly-refreshed on free/pro keys, real-time on enterprise (Team) keys. Provider-level price discovery across corridors like GBP/NGN, USD/KES and USD/XOF: latest rates, conversion, historical time series, corridor coverage and provider metadata. Authenticate with an X-API-Key header — sign up free at https://modan.io/signup and your first key is minted automatically. spread_bps is the distance in basis points below the best rate observed in the corridor of the same rate_type (not vs an independent mid-market rate); rate_type distinguishes official, interbank, retail, p2p and parallel prices, which are not substitutes for one another. AI agents: a native MCP server (streamable HTTP) exposes the same data as tools at https://modan.io/api/mcp — see https://modan.io/llms-full.txt for the complete LLM-readable reference.",
    "version": "1.0.0",
    "contact": {
      "name": "Modan",
      "url": "https://modan.io"
    }
  },
  "servers": [
    {
      "url": "https://modan.io/api/v1"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/rates": {
      "get": {
        "operationId": "getRates",
        "summary": "Latest provider rates for a currency corridor",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GBP"
            },
            "description": "Source currency code"
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            },
            "description": "Target currency code"
          }
        ],
        "responses": {
          "200": {
            "description": "Current rates from all active providers covering the corridor",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RatesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "summary": "Ingest rate observations (data-team only)",
        "description": "Record provider rate observations. Requires an API key whose owning account holds the admin or treasury role; regular data keys receive 403. Accepts one object or an array of up to 100. The batch is atomic: any invalid row rejects the whole request (422, per-row errors) and nothing is inserted. Ingestion does not consume the daily read quota.",
        "operationId": "ingestRates",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/IngestRate"
                  },
                  {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/IngestRate"
                    },
                    "maxItems": 100
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "All rows inserted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "inserted": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed body"
          },
          "401": {
            "description": "API key missing, invalid, or revoked"
          },
          "403": {
            "description": "Key's account lacks the admin/treasury role"
          },
          "422": {
            "description": "Validation failed; nothing inserted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer"
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/rates/history": {
      "get": {
        "operationId": "getRateHistory",
        "summary": "Historical rate time series for a corridor",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GBP"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "7d",
                "30d",
                "90d"
              ],
              "default": "30d"
            }
          },
          {
            "name": "provider",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "wise"
            },
            "description": "Filter to a single provider id"
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            },
            "description": "Chronological order; asc (oldest first) suits time-series charting"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 500,
              "maximum": 5000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Timestamped rate observations (paginated; has_more indicates further pages)",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoryResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/convert": {
      "get": {
        "operationId": "convert",
        "summary": "Convert an amount across a corridor, per provider (net of fees)",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GBP"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "example": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Per-provider converted and net-of-fee amounts, the best value, and the mid-converted amount when available",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConvertResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/currencies": {
      "get": {
        "operationId": "getCurrencies",
        "summary": "Active currencies and the corridors currently served",
        "responses": {
          "200": {
            "description": "Active currencies plus the distinct corridors with live rates",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrenciesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/corridors": {
      "get": {
        "operationId": "getCorridors",
        "summary": "All available currency corridors with best rates",
        "responses": {
          "200": {
            "description": "Corridor summaries",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorridorsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/providers": {
      "get": {
        "operationId": "getProviders",
        "summary": "All active providers with metadata",
        "responses": {
          "200": {
            "description": "Active providers",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProvidersResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/fetch-one": {
      "get": {
        "operationId": "fetchOne",
        "summary": "Mid-market rate for one pair, plus the best tracked provider when covered",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "USD"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mid + provider_best for the pair",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FetchOneResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/fetch-multi": {
      "get": {
        "operationId": "fetchMulti",
        "summary": "One base against up to 20 quote currencies in a single call (one quota unit)",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "USD"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN,KES,GHS",
              "description": "Comma-separated ISO-4217 codes"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Per-quote mid + provider_best",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FetchMultiResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/fetch-matrix": {
      "get": {
        "operationId": "fetchMatrix",
        "summary": "Full cross matrix, up to 10 bases x 10 quotes (one quota unit)",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "USD,GBP",
              "description": "Comma-separated ISO-4217 codes (max 10)"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN,KES",
              "description": "Comma-separated ISO-4217 codes (max 10)"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Nested base->quote map of mid + provider_best",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FetchMatrixResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/fetch-many-to-one": {
      "get": {
        "operationId": "fetchManyToOne",
        "summary": "Many base currencies into a single quote currency (one quota unit)",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "USD,GBP,CAD",
              "description": "Comma-separated ISO-4217 codes (max 20)"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Per-base mid + provider_best into the quote currency",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FetchManyToOneResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/time-series": {
      "get": {
        "operationId": "getTimeSeries",
        "summary": "Bucketed daily/hourly series of mid + best provider rate",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GBP"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            }
          },
          {
            "name": "interval",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "P1D",
                "PT1H"
              ],
              "default": "P1D"
            },
            "description": "ISO-8601 bucket size: P1D daily (max 366 buckets) or PT1H hourly (max 168)"
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "7d",
                "30d",
                "90d"
              ],
              "default": "30d"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Explicit window start (overrides period)"
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Explicit window end (defaults to now)"
          }
        ],
        "responses": {
          "200": {
            "description": "Series of { t, mid, best, samples } buckets",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimeSeriesResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/historical": {
      "get": {
        "operationId": "getHistorical",
        "summary": "Corridor snapshot as of a past date",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GBP"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-07-01"
            },
            "description": "As-of date (YYYY-MM-DD, UTC, not in the future)"
          }
        ],
        "responses": {
          "200": {
            "description": "Each provider's latest quote on or before the date, plus mid when covered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoricalResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/change": {
      "get": {
        "operationId": "getChange",
        "summary": "Absolute and percent change of mid + best rate over a period",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GBP"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NGN"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "7d",
                "30d",
                "90d"
              ],
              "default": "7d"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Start/end values and change legs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChangeResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/rates/provider": {
      "get": {
        "operationId": "getProviderRates",
        "summary": "Every corridor and current rate one provider quotes",
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "lemfi"
            },
            "description": "A provider_id from GET /providers"
          }
        ],
        "responses": {
          "200": {
            "description": "Provider metadata + all quoted corridors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProviderRatesResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "X-Data-Freshness": {
                "$ref": "#/components/headers/XDataFreshness"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Unknown or inactive provider",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/admin/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Your account's usage, quota and per-key breakdown (does not consume quota)",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Metering state for the calling account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Public platform health (no API key, never consumes quota)",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Platform health snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Create a free API key at https://modan.io/signup (terminal → Developers → My Keys)."
      }
    },
    "headers": {
      "XRateLimitLimit": {
        "description": "Your plan's daily request allowance (free 50 / pro 250 / enterprise 1,000).",
        "schema": {
          "type": "integer"
        }
      },
      "XRateLimitRemaining": {
        "description": "Requests remaining in today's UTC window.",
        "schema": {
          "type": "integer"
        }
      },
      "XRateLimitReset": {
        "description": "Epoch seconds at which the daily window resets (next UTC midnight).",
        "schema": {
          "type": "integer"
        }
      },
      "XDataFreshness": {
        "description": "hourly = served from the top-of-the-current-UTC-hour snapshot (free/pro keys); realtime = live data (enterprise keys).",
        "schema": {
          "type": "string",
          "enum": [
            "hourly",
            "realtime"
          ]
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Missing required query parameters",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "API key missing, invalid, or revoked",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Daily rate limit exceeded for your tier (free 50/day, pro 250/day, enterprise 1,000/day). Data freshness is also tiered: free/pro serve rates as of the top of the current UTC hour (data_freshness: hourly + as_of in responses); enterprise is real-time.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "ProviderRate": {
        "type": "object",
        "properties": {
          "provider_id": {
            "type": "string",
            "example": "wise"
          },
          "provider_name": {
            "type": "string",
            "example": "Wise"
          },
          "rate": {
            "type": "number",
            "example": 2045.5
          },
          "fee": {
            "type": [
              "number",
              "null"
            ],
            "example": 2.99
          },
          "fee_currency": {
            "type": [
              "string",
              "null"
            ],
            "example": "GBP"
          },
          "spread_bps": {
            "type": "number",
            "example": 24.5,
            "description": "Basis points below the best rate in the corridor of the SAME rate_type (0 = best of its kind). Not a spread against the independent mid."
          },
          "vs_mid_bps": {
            "type": "number",
            "example": -12.3,
            "description": "Basis points vs the independent mid (negative = below mid). Present only when a recent reference mid exists for the corridor."
          },
          "transfer_time": {
            "type": [
              "string",
              "null"
            ],
            "example": "1 - 2 business days"
          },
          "last_updated": {
            "type": "string",
            "format": "date-time"
          },
          "rate_type": {
            "type": "string",
            "enum": [
              "official",
              "interbank",
              "retail",
              "p2p",
              "parallel"
            ],
            "description": "What KIND of price this is. Ranking across kinds is meaningless: a central bank's official reference is real and not obtainable, so it is never the corridor's best rate and never the best value on /convert. Absent means retail."
          }
        }
      },
      "RatesResponse": {
        "type": "object",
        "properties": {
          "corridor": {
            "type": "string",
            "example": "GBP/NGN"
          },
          "providers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProviderRate"
            }
          },
          "count": {
            "type": "integer"
          },
          "mid_rate": {
            "type": "number",
            "description": "Independent mid-market rate for the corridor. Present only when a recent reference mid exists."
          },
          "mid_source": {
            "type": "string",
            "example": "open.er-api.com"
          },
          "mid_fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "ConvertProviderEntry": {
        "type": "object",
        "properties": {
          "provider_id": {
            "type": "string"
          },
          "provider_name": {
            "type": "string"
          },
          "rate": {
            "type": "number"
          },
          "fee": {
            "type": [
              "number",
              "null"
            ]
          },
          "fee_currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "converted": {
            "type": "number",
            "description": "amount * rate, before fees, in the target currency"
          },
          "net_converted": {
            "type": "number",
            "description": "converted minus fee, in the target currency"
          },
          "spread_bps": {
            "type": "number",
            "description": "Basis points below the best rate in the corridor of the SAME rate_type (0 = best of its kind). Not a spread against the independent mid."
          },
          "vs_mid_bps": {
            "type": "number"
          },
          "rate_type": {
            "type": "string",
            "enum": [
              "official",
              "interbank",
              "retail",
              "p2p",
              "parallel"
            ],
            "description": "What KIND of price this is. Ranking across kinds is meaningless: a central bank's official reference is real and not obtainable, so it is never the corridor's best rate and never the best value on /convert. Absent means retail."
          }
        }
      },
      "ConvertResponse": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "example": "GBP"
          },
          "to": {
            "type": "string",
            "example": "NGN"
          },
          "amount": {
            "type": "number",
            "example": 1000
          },
          "mid": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "rate": {
                "type": "number"
              },
              "converted": {
                "type": "number"
              },
              "source": {
                "type": "string"
              },
              "fetched_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "best": {
            "$ref": "#/components/schemas/ConvertProviderEntry"
          },
          "providers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConvertProviderEntry"
            }
          },
          "count": {
            "type": "integer"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "CurrenciesResponse": {
        "type": "object",
        "properties": {
          "currencies": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "example": "NGN"
                },
                "name": {
                  "type": "string",
                  "example": "Nigerian Naira"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "source",
                    "target",
                    "both"
                  ]
                },
                "flag_url": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "is_active": {
                  "type": "boolean"
                },
                "sort_order": {
                  "type": "integer"
                }
              }
            }
          },
          "corridors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "from": {
                  "type": "string"
                },
                "to": {
                  "type": "string"
                }
              }
            }
          },
          "currency_count": {
            "type": "integer"
          },
          "corridor_count": {
            "type": "integer"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HistoryPoint": {
        "type": "object",
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "provider_id": {
            "type": "string"
          },
          "provider_name": {
            "type": "string"
          },
          "rate": {
            "type": "number"
          },
          "fee": {
            "type": [
              "number",
              "null"
            ]
          },
          "fee_currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "spread_bps": {
            "type": [
              "number",
              "null"
            ],
            "description": "Basis points below the best rate in the corridor of the SAME rate_type (0 = best of its kind). Not a spread against the independent mid."
          },
          "rate_type": {
            "type": "string",
            "enum": [
              "official",
              "interbank",
              "retail",
              "p2p",
              "parallel"
            ],
            "description": "What KIND of price this is. Ranking across kinds is meaningless: a central bank's official reference is real and not obtainable, so it is never the corridor's best rate and never the best value on /convert. Absent means retail."
          }
        }
      },
      "HistoryResponse": {
        "type": "object",
        "properties": {
          "corridor": {
            "type": "string",
            "example": "GBP/NGN"
          },
          "period": {
            "type": "string",
            "example": "30d"
          },
          "from_date": {
            "type": "string",
            "format": "date-time"
          },
          "to_date": {
            "type": "string",
            "format": "date-time"
          },
          "order": {
            "type": "string",
            "enum": [
              "asc",
              "desc"
            ]
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HistoryPoint"
            }
          },
          "count": {
            "type": "integer"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "Corridor": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "example": "GBP"
          },
          "to": {
            "type": "string",
            "example": "NGN"
          },
          "provider_count": {
            "type": "integer"
          },
          "best_rate": {
            "type": "number"
          },
          "last_updated": {
            "type": "string",
            "format": "date-time"
          },
          "avg_spread_bps": {
            "type": [
              "number",
              "null"
            ],
            "description": "Average spread across executable quotes in the corridor, measured within each rate_type. Non-executable prices (official, parallel) are excluded."
          }
        }
      },
      "CorridorsResponse": {
        "type": "object",
        "properties": {
          "corridors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Corridor"
            }
          },
          "count": {
            "type": "integer"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "Provider": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "wise"
          },
          "name": {
            "type": "string",
            "example": "Wise"
          },
          "logo_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "website_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider_type": {
            "type": [
              "string",
              "null"
            ],
            "example": "imto",
            "enum": [
              "central_bank",
              "commercial_bank",
              "non_bank_lp",
              "imto",
              "fintech_psp",
              "crypto_venue",
              "bureau_de_change",
              "aggregator"
            ],
            "description": "What kind of institution published the price. Orthogonal to rate_type. 'aggregator' republishes another source — derived, not observed."
          },
          "rate_type": {
            "type": "string",
            "enum": [
              "official",
              "interbank",
              "retail",
              "p2p",
              "parallel"
            ],
            "example": "retail",
            "description": "What KIND of price this provider publishes. Orthogonal to provider_type. official and parallel are not executable: they are returned and labelled, but never chosen as a corridor's best rate."
          },
          "region": {
            "type": [
              "string",
              "null"
            ]
          },
          "transfer_time": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_methods": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ProvidersResponse": {
        "type": "object",
        "properties": {
          "providers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Provider"
            }
          },
          "count": {
            "type": "integer"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IngestRate": {
        "type": "object",
        "required": [
          "provider_id",
          "rate"
        ],
        "properties": {
          "provider_id": {
            "type": "string",
            "description": "Active provider id (see GET /providers)"
          },
          "from": {
            "type": "string",
            "description": "Alias for source_currency (ISO-4217)"
          },
          "to": {
            "type": "string",
            "description": "Alias for target_currency (ISO-4217)"
          },
          "source_currency": {
            "type": "string"
          },
          "target_currency": {
            "type": "string"
          },
          "rate": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "fee": {
            "type": "number",
            "minimum": 0
          },
          "fee_currency": {
            "type": "string",
            "description": "Defaults to the source currency when a fee is given"
          },
          "notes": {
            "type": "string",
            "maxLength": 500
          },
          "effective_from": {
            "type": "string",
            "format": "date-time",
            "description": "Defaults to now; max 5y backfill, no future timestamps"
          }
        }
      },
      "PairValue": {
        "type": "object",
        "description": "Mid-market rate for a pair plus the best tracked provider when the pair is a covered corridor",
        "properties": {
          "mid": {
            "type": "number",
            "description": "Independent mid-market rate (cross-computed through the freshest USD reference snapshot)"
          },
          "provider_best": {
            "type": [
              "object",
              "null"
            ],
            "description": "Best tracked provider payout — null when the pair is not a covered corridor",
            "properties": {
              "rate": {
                "type": "number"
              },
              "provider_id": {
                "type": "string"
              },
              "last_updated": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        },
        "required": [
          "mid",
          "provider_best"
        ]
      },
      "FetchOneResponse": {
        "type": "object",
        "properties": {
          "base": {
            "type": "string"
          },
          "quote": {
            "type": "string"
          },
          "mid": {
            "type": "number"
          },
          "provider_best": {
            "$ref": "#/components/schemas/PairValue/properties/provider_best"
          },
          "source": {
            "type": "string"
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "FetchMultiResponse": {
        "type": "object",
        "properties": {
          "base": {
            "type": "string"
          },
          "results": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/PairValue"
            }
          },
          "count": {
            "type": "integer"
          },
          "source": {
            "type": "string"
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "FetchMatrixResponse": {
        "type": "object",
        "properties": {
          "bases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "quotes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "results": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "additionalProperties": {
                "$ref": "#/components/schemas/PairValue"
              }
            }
          },
          "source": {
            "type": "string"
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "FetchManyToOneResponse": {
        "type": "object",
        "properties": {
          "quote": {
            "type": "string"
          },
          "results": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/PairValue"
            }
          },
          "count": {
            "type": "integer"
          },
          "source": {
            "type": "string"
          },
          "fetched_at": {
            "type": "string",
            "format": "date-time"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "TimeSeriesPoint": {
        "type": "object",
        "properties": {
          "t": {
            "type": "string",
            "format": "date-time",
            "description": "Bucket start (UTC)"
          },
          "mid": {
            "type": [
              "number",
              "null"
            ],
            "description": "Last reference mid in the bucket (null before the feed's history begins)"
          },
          "best": {
            "type": [
              "number",
              "null"
            ],
            "description": "Best tracked provider rate observed in the bucket"
          },
          "samples": {
            "type": "integer",
            "description": "Provider observations in the bucket"
          }
        }
      },
      "TimeSeriesResponse": {
        "type": "object",
        "properties": {
          "corridor": {
            "type": "string"
          },
          "interval": {
            "type": "string",
            "enum": [
              "P1D",
              "PT1H"
            ]
          },
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          },
          "tracked_corridor": {
            "type": "boolean"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimeSeriesPoint"
            }
          },
          "count": {
            "type": "integer"
          },
          "note": {
            "type": "string",
            "description": "Present when the window was clamped or the pair has no tracked providers"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "HistoricalResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/RatesResponse"
          },
          {
            "type": "object",
            "properties": {
              "date": {
                "type": "string",
                "format": "date",
                "description": "Requested as-of date"
              },
              "as_of": {
                "type": "string",
                "format": "date-time",
                "description": "End-of-day UTC cutoff used"
              }
            }
          }
        ]
      },
      "ChangeLeg": {
        "type": "object",
        "properties": {
          "abs": {
            "type": [
              "number",
              "null"
            ]
          },
          "pct": {
            "type": [
              "number",
              "null"
            ],
            "description": "Percent change"
          }
        }
      },
      "ChangeResponse": {
        "type": "object",
        "properties": {
          "corridor": {
            "type": "string"
          },
          "period": {
            "type": "string"
          },
          "start": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "mid": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "best": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "end": {
            "type": "object",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "mid": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "best": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "change": {
            "type": "object",
            "properties": {
              "mid": {
                "$ref": "#/components/schemas/ChangeLeg"
              },
              "best": {
                "$ref": "#/components/schemas/ChangeLeg"
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "ProviderRatesResponse": {
        "type": "object",
        "properties": {
          "provider": {
            "$ref": "#/components/schemas/Provider"
          },
          "corridors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "from": {
                  "type": "string"
                },
                "to": {
                  "type": "string"
                },
                "rate": {
                  "type": "number"
                },
                "fee": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "fee_currency": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "last_updated": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "count": {
            "type": "integer"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data_freshness": {
            "type": "string",
            "enum": [
              "hourly",
              "realtime"
            ],
            "description": "Freshness tier this response was served at: hourly (free/pro keys — rates as of the top of the current UTC hour) or realtime (enterprise keys)."
          },
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "Present when data_freshness is hourly: the snapshot cutoff (top of the current UTC hour)."
          }
        }
      },
      "UsageResponse": {
        "type": "object",
        "description": "Account metering state. Calling this endpoint does not consume quota.",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "pro",
              "enterprise"
            ]
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          },
          "limit": {
            "type": "integer"
          },
          "used": {
            "type": "integer"
          },
          "remaining": {
            "type": "integer"
          },
          "reset": {
            "type": "integer",
            "description": "Epoch seconds of next UTC midnight"
          },
          "key": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "keys_today": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key_id": {
                  "type": "string"
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "requests": {
                  "type": "integer"
                }
              }
            }
          },
          "daily_history": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "requests": {
                  "type": "integer"
                }
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "description": "Public platform health. No API key required; never consumes quota.",
        "properties": {
          "status": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "corridors": {
            "type": "integer"
          },
          "providers": {
            "type": "integer"
          },
          "last_rate_update": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "mid_feed": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "source": {
                "type": "string"
              },
              "last_fetched": {
                "type": "string",
                "format": "date-time"
              },
              "age_seconds": {
                "type": "integer"
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
