{
  "openapi": "3.0.3",
  "info": {
    "title": "Vitati360 External API",
    "version": "1.1.0",
    "description": "Public financial data API. Authenticate every request with an API key sent in the `x-api-key` header. Keys are issued by the Vitati360 team. See the Getting started and Errors and rate limits pages for details.",
    "contact": {
      "name": "Vitati360",
      "url": "https://vitati360.com/contact"
    }
  },
  "servers": [
    {
      "url": "https://api.vitati360.com/proxy",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Companies",
      "description": "Find a company and read its profile and metrics"
    },
    {
      "name": "Financials",
      "description": "Financial history of one company"
    }
  ],
  "paths": {
    "/external/v1/companies": {
      "get": {
        "tags": [
          "Companies"
        ],
        "summary": "Search companies",
        "description": "Finds companies by ticker or name. Only companies with verified data are returned. Use the returned `country` and `ticker` in the other endpoints.",
        "operationId": "searchCompanies",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Ticker or company name, at least 2 characters. Matches anywhere in the name and in the ticker. Case-insensitive.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 80,
              "example": "qnb"
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "Restrict results to one country code, for example `qa`.",
            "schema": {
              "type": "string",
              "pattern": "^[a-zA-Z]{2,3}$",
              "example": "qa"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of results.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching companies. The most exact ticker match comes first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CompanySearchResult"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/external/v1/company/{country}/{ticker}": {
      "get": {
        "tags": [
          "Companies"
        ],
        "summary": "Company profile",
        "description": "Returns one company's profile. It contains no financial values; use the financials endpoint for those.",
        "operationId": "getCompanyProfile",
        "parameters": [
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "$ref": "#/components/parameters/Ticker"
          }
        ],
        "responses": {
          "200": {
            "description": "The company profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompanyProfile"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/external/v1/company/{country}/{ticker}/metrics": {
      "get": {
        "tags": [
          "Companies"
        ],
        "summary": "Company metrics",
        "description": "Lists the metrics this company reports, grouped by section. Use these keys to read values from the financials endpoint. The list varies by company.",
        "operationId": "getCompanyMetrics",
        "parameters": [
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "$ref": "#/components/parameters/Ticker"
          }
        ],
        "responses": {
          "200": {
            "description": "Metric sections for the company.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MetricSection"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/external/v1/company/{country}/{ticker}/financials": {
      "get": {
        "tags": [
          "Financials"
        ],
        "summary": "Financial history",
        "description": "Returns the financial history of one company: annual reports, quarterly reports, or both. Each report carries the statement sections and their values for that fiscal period.\n\nWithout `currency`, values are returned as reported. With `currency`, monetary values are converted to that currency. Share counts, ratios and other non-monetary values are never converted. If a rate is not available for a value, that value is returned as reported.\n\n`sections` and the year range narrow the response. They do not change the values.",
        "operationId": "getFinancials",
        "parameters": [
          {
            "$ref": "#/components/parameters/Country"
          },
          {
            "$ref": "#/components/parameters/Ticker"
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "description": "Which report arrays to include. `all` returns annual and quarterly reports.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "annual",
                "quarterly"
              ],
              "default": "all"
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "ISO 4217 currency code to convert monetary values to, for example `USD`. Omit to receive values as reported.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{3}$",
              "example": "USD"
            }
          },
          {
            "name": "sections",
            "in": "query",
            "required": false,
            "description": "Comma-separated statement sections to return, for example `incomeStatement,balanceSheet`. Omit to return every section. Sections the company does not report are left out.",
            "schema": {
              "type": "string",
              "maxLength": 400,
              "example": "incomeStatement,balanceSheet"
            }
          },
          {
            "name": "fromYear",
            "in": "query",
            "required": false,
            "description": "First fiscal year to include, inclusive.",
            "schema": {
              "type": "integer",
              "minimum": 1900,
              "maximum": 2100,
              "example": 2020
            }
          },
          {
            "name": "toYear",
            "in": "query",
            "required": false,
            "description": "Last fiscal year to include, inclusive. Must not be earlier than `fromYear`.",
            "schema": {
              "type": "integer",
              "minimum": 1900,
              "maximum": 2100,
              "example": 2025
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Financial history for the company.",
            "headers": {
              "Cache-Control": {
                "description": "Responses may be cached for 5 minutes, with stale responses allowed for up to a day while they refresh.",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=300, stale-while-revalidate=86400"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FinancialsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your Vitati360 API key."
      }
    },
    "parameters": {
      "Country": {
        "name": "country",
        "in": "path",
        "required": true,
        "description": "Country code, for example `qa`. Case-insensitive.",
        "schema": {
          "type": "string",
          "example": "qa"
        }
      },
      "Ticker": {
        "name": "ticker",
        "in": "path",
        "required": true,
        "description": "Ticker symbol, for example `qnbk`. Case-insensitive.",
        "schema": {
          "type": "string",
          "example": "qnbk"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "A query parameter is invalid. The message says which one.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key is missing, does not match a key, is disabled, or has expired.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No company matches the country and ticker.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "The API key has sent more requests than its limit in the current minute.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "CompanySearchResult": {
        "type": "object",
        "properties": {
          "ticker": {
            "type": "string",
            "example": "qnbk"
          },
          "name": {
            "type": "string",
            "example": "Qatar National Bank"
          },
          "country": {
            "type": "string",
            "example": "qa"
          },
          "exchange": {
            "type": "string",
            "example": "QSE"
          },
          "sector": {
            "type": "string",
            "nullable": true,
            "example": "Financials"
          },
          "reportedCurrency": {
            "type": "string",
            "example": "QAR"
          }
        }
      },
      "CompanyProfile": {
        "type": "object",
        "properties": {
          "ticker": {
            "type": "string",
            "example": "qnbk"
          },
          "name": {
            "type": "string",
            "example": "Qatar National Bank"
          },
          "country": {
            "type": "string",
            "example": "qa"
          },
          "region": {
            "type": "string",
            "example": "mena"
          },
          "exchange": {
            "type": "string",
            "example": "QSE"
          },
          "sector": {
            "type": "string",
            "nullable": true,
            "example": "Financials"
          },
          "subsector": {
            "type": "string",
            "nullable": true,
            "example": "Banks"
          },
          "reportedCurrency": {
            "type": "string",
            "example": "QAR"
          },
          "fiscalYearEnd": {
            "type": "string"
          }
        }
      },
      "MetricSection": {
        "type": "object",
        "properties": {
          "section": {
            "type": "string",
            "description": "The section name, as used in the `sections` parameter and the financials response.",
            "example": "incomeStatement"
          },
          "label": {
            "type": "string",
            "example": "Income statement"
          },
          "metrics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "example": "revenue"
                },
                "label": {
                  "type": "string",
                  "example": "Revenue"
                },
                "unitClass": {
                  "type": "string",
                  "nullable": true,
                  "description": "`MONETARY` and `MONETARY_PER_SHARE` values are converted by `currency`. Other values are not.",
                  "example": "MONETARY"
                },
                "unit": {
                  "type": "string",
                  "nullable": true,
                  "example": "QAR"
                }
              }
            }
          }
        }
      },
      "FinancialsResponse": {
        "type": "object",
        "required": [
          "company",
          "reportedCurrency",
          "displayCurrency"
        ],
        "properties": {
          "company": {
            "type": "object",
            "properties": {
              "ticker": {
                "type": "string",
                "example": "qnbk"
              },
              "name": {
                "type": "string",
                "example": "Qatar National Bank"
              },
              "country": {
                "type": "string",
                "example": "qa"
              },
              "region": {
                "type": "string",
                "nullable": true,
                "example": "mena"
              }
            }
          },
          "reportedCurrency": {
            "type": "string",
            "nullable": true,
            "example": "QAR"
          },
          "displayCurrency": {
            "type": "string",
            "nullable": true,
            "description": "The currency the values are expressed in. Equals `reportedCurrency` unless `currency` was requested.",
            "example": "USD"
          },
          "annualReports": {
            "type": "array",
            "description": "Present when `period` is `all` or `annual`. Newest first.",
            "items": {
              "$ref": "#/components/schemas/FinancialReport"
            }
          },
          "quarterlyReports": {
            "type": "array",
            "description": "Present when `period` is `all` or `quarterly`. Newest first.",
            "items": {
              "$ref": "#/components/schemas/FinancialReport"
            }
          }
        }
      },
      "FinancialReport": {
        "type": "object",
        "description": "One fiscal period. Statement sections (for example `incomeStatement`, `balanceSheet`, `cashFlowStatement`) are added as further fields, each an object keyed by metric name. Use the metrics endpoint to list the metric names for a company.",
        "required": [
          "fiscalYear",
          "fiscalPeriod"
        ],
        "properties": {
          "fiscalYear": {
            "type": "integer",
            "example": 2025
          },
          "fiscalPeriod": {
            "type": "string",
            "description": "`FY` for annual reports; `Q1` to `Q4` for quarterly reports.",
            "example": "FY"
          },
          "periodEnd": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "example": "2025-12-31"
          },
          "filedAt": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "example": "2026-02-10"
          }
        },
        "additionalProperties": {
          "description": "A statement section: an object of metric values, or a scalar."
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "example": "Rate limit exceeded for this API key"
          },
          "error": {
            "type": "string",
            "description": "A short name for the error type."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-23T10:15:00.000Z"
          },
          "statusCode": {
            "type": "integer",
            "example": 429
          }
        }
      }
    }
  }
}
