{
  "openapi": "3.1.0",
  "info": {
    "title": "Data Pay Index API",
    "version": "1.0.0",
    "summary": "Read-only data & AI skills demand and pay aggregates from public job-board APIs.",
    "description": "Free, read-only JSON API over the Data Pay Index: which skills data, analytics and AI employers ask for in open job postings, and the median pay those postings publish. Aggregates only (no postings, titles or per-company data). Rebuilt daily from the latest snapshot; every response carries `as_of`. No authentication. CORS is open for GET. Errors are RFC 9457 problem details (`application/problem+json`). Rate limit: 120 requests/minute per client address, enforced per server instance (best effort), advertised with the IETF `RateLimit` and `RateLimit-Policy` headers. Versioning: breaking changes get a new `/api/v2/`; `/api/v1/` stays stable.",
    "contact": {
      "name": "Data Pay Index",
      "email": "hello@datapayindex.com",
      "url": "https://datapayindex.com/contact/"
    },
    "license": {
      "name": "CC BY 4.0 (data)",
      "identifier": "CC-BY-4.0"
    },
    "termsOfService": "https://datapayindex.com/terms/"
  },
  "externalDocs": {
    "description": "API and MCP server docs",
    "url": "https://datapayindex.com/api/"
  },
  "servers": [
    {
      "url": "https://datapayindex.com/api/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Dataset"
    },
    {
      "name": "Skills"
    },
    {
      "name": "Roles"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "Dataset"
        ],
        "summary": "API index",
        "description": "Lists the endpoints and the dataset date.",
        "responses": {
          "200": {
            "description": "API index",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/meta": {
      "get": {
        "operationId": "getDatasetMeta",
        "tags": [
          "Dataset"
        ],
        "summary": "Dataset summary",
        "description": "Data date, coverage (postings, companies, job boards), pay rules, license and citation.",
        "responses": {
          "200": {
            "description": "Dataset summary",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Meta"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/skills": {
      "get": {
        "operationId": "listSkills",
        "tags": [
          "Skills"
        ],
        "summary": "List and filter skills",
        "description": "All tracked skills with demand and median pay. Filter by category, role, name, minimum postings or minimum pay sample; sort and limit. With `role`, each item also has `role_postings` and `pct_of_role`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/category"
          },
          {
            "$ref": "#/components/parameters/role"
          },
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "$ref": "#/components/parameters/min_postings"
          },
          {
            "$ref": "#/components/parameters/min_pay_n"
          },
          {
            "$ref": "#/components/parameters/sort"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Skills",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/skills/{key}": {
      "get": {
        "operationId": "getSkill",
        "tags": [
          "Skills"
        ],
        "summary": "One skill",
        "description": "Demand, median pay, pay by level, and the roles that ask for one skill. `key` also accepts the display name (case-insensitive).",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Skill key, e.g. `databricks`, `dbt`, `python`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Skill",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/roles": {
      "get": {
        "operationId": "listRoles",
        "tags": [
          "Roles"
        ],
        "summary": "List roles",
        "description": "The 12 data & AI role families with postings and median pay.",
        "responses": {
          "200": {
            "description": "Roles",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoleList"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/roles/{key}": {
      "get": {
        "operationId": "getRole",
        "tags": [
          "Roles"
        ],
        "summary": "One role",
        "description": "Postings, median pay, pay by level and the most-requested skills for one role family.",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "description": "Role key, e.g. `data-engineer`, `ai-engineer`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Number of top skills to return (1-25, default all available, at most 25).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Role",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoleResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/levels": {
      "get": {
        "operationId": "listLevels",
        "tags": [
          "Roles"
        ],
        "summary": "Pay by level",
        "description": "Postings and median published US pay by seniority level (from job titles), all data & AI roles.",
        "responses": {
          "200": {
            "description": "Levels",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LevelList"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/categories": {
      "get": {
        "operationId": "listCategories",
        "tags": [
          "Skills"
        ],
        "summary": "Skill categories",
        "description": "Taxonomy categories and how many skills each has.",
        "responses": {
          "200": {
            "description": "Categories",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryList"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "ETag": {
        "description": "Weak validator; send If-None-Match for a 304.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit": {
        "description": "IETF draft RateLimit field: remaining quota (r) and seconds to reset (t).",
        "schema": {
          "type": "string"
        }
      },
      "RateLimitPolicy": {
        "description": "IETF draft RateLimit-Policy field: quota (q) per window (w seconds).",
        "schema": {
          "type": "string"
        }
      }
    },
    "parameters": {
      "category": {
        "name": "category",
        "in": "query",
        "required": false,
        "description": "Only skills in this taxonomy category (see /categories), e.g. `platform`, `genai`.",
        "schema": {
          "type": "string"
        }
      },
      "role": {
        "name": "role",
        "in": "query",
        "required": false,
        "description": "Only skills mentioned in postings for this role family (key or name), e.g. `data-engineer`.",
        "schema": {
          "type": "string"
        }
      },
      "q": {
        "name": "q",
        "in": "query",
        "required": false,
        "description": "Case-insensitive match on skill key or name.",
        "schema": {
          "type": "string"
        }
      },
      "min_postings": {
        "name": "min_postings",
        "in": "query",
        "required": false,
        "description": "Minimum postings.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "min_pay_n": {
        "name": "min_pay_n",
        "in": "query",
        "required": false,
        "description": "Minimum postings with pay (use 10 to get only skills with a median).",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "sort": {
        "name": "sort",
        "in": "query",
        "required": false,
        "description": "Sort order (descending except name). `role_postings` needs `role`.",
        "schema": {
          "type": "string",
          "enum": [
            "postings",
            "share",
            "companies",
            "median_pay",
            "wow_change",
            "name",
            "role_postings"
          ],
          "default": "postings"
        }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Maximum items (1-500).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 500
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid parameter",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "Unknown skill, role or endpoint",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limited; see Retry-After",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details.",
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "URI of the error description on the docs page."
          },
          "title": {
            "type": "string",
            "description": "Short summary."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status."
          },
          "detail": {
            "type": "string",
            "description": "What went wrong."
          },
          "instance": {
            "type": "string",
            "description": "Request path."
          },
          "code": {
            "type": "string",
            "description": "Stable error code.",
            "enum": [
              "not_found",
              "skill_not_found",
              "role_not_found",
              "invalid_parameter",
              "method_not_allowed",
              "rate_limited",
              "internal_error"
            ]
          },
          "hint": {
            "type": "string",
            "description": "What to do next."
          },
          "suggestions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Close matches for an unknown key."
          }
        }
      },
      "Envelope": {
        "type": "object",
        "required": [
          "as_of",
          "week",
          "source"
        ],
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date",
            "description": "Date of the snapshot the figures come from."
          },
          "week": {
            "type": "string",
            "description": "ISO week of the figures, e.g. 2026-W43."
          },
          "source": {
            "type": "string",
            "description": "Citation line to quote with any figure."
          }
        }
      },
      "Skill": {
        "type": "object",
        "required": [
          "key",
          "name",
          "category",
          "postings",
          "share_pct",
          "companies",
          "median_pay_usd",
          "pay_n"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Stable skill key, used in URLs (for example `databricks`)."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "category": {
            "type": "string",
            "description": "Taxonomy category (see /api/v1/categories)."
          },
          "postings": {
            "type": "integer",
            "description": "Open data & AI postings that mention the skill."
          },
          "share_pct": {
            "type": "number",
            "description": "Percent of all tracked data & AI postings that mention the skill."
          },
          "companies": {
            "type": "integer",
            "description": "Distinct companies with at least one such posting."
          },
          "median_pay_usd": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Median published US pay (USD, annualized range midpoint). null when fewer than min_n postings have pay."
          },
          "pay_n": {
            "type": "integer",
            "description": "US postings with published USD pay behind the median."
          },
          "wow_change_pts": {
            "type": [
              "number",
              "null"
            ],
            "description": "Week-over-week change in share, percentage points. null until two weeks of history exist."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human page for the skill on datapayindex.com (null for skills with fewer than 5 postings)."
          },
          "role_postings": {
            "type": "integer",
            "description": "Only with ?role: postings in that role that mention the skill."
          },
          "pct_of_role": {
            "type": "number",
            "description": "Only with ?role: percent of that role's postings."
          }
        }
      },
      "SkillDetail": {
        "type": "object",
        "required": [
          "key",
          "name",
          "category",
          "postings",
          "share_pct",
          "companies",
          "median_pay_usd",
          "pay_n",
          "pay_by_level",
          "roles"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Stable skill key, used in URLs (for example `databricks`)."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "category": {
            "type": "string",
            "description": "Taxonomy category (see /api/v1/categories)."
          },
          "postings": {
            "type": "integer",
            "description": "Open data & AI postings that mention the skill."
          },
          "share_pct": {
            "type": "number",
            "description": "Percent of all tracked data & AI postings that mention the skill."
          },
          "companies": {
            "type": "integer",
            "description": "Distinct companies with at least one such posting."
          },
          "median_pay_usd": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Median published US pay (USD, annualized range midpoint). null when fewer than min_n postings have pay."
          },
          "pay_n": {
            "type": "integer",
            "description": "US postings with published USD pay behind the median."
          },
          "wow_change_pts": {
            "type": [
              "number",
              "null"
            ],
            "description": "Week-over-week change in share, percentage points. null until two weeks of history exist."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human page for the skill on datapayindex.com (null for skills with fewer than 5 postings)."
          },
          "pay_by_level": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "level",
                "level_name",
                "median_pay_usd",
                "pay_n"
              ],
              "properties": {
                "level": {
                  "type": "string",
                  "description": "Level key from the job title (intern, entry, mid, senior, staff, principal, manager, director_plus, unspecified)."
                },
                "level_name": {
                  "type": "string",
                  "description": "Display name."
                },
                "median_pay_usd": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Median published US pay at this level; null when n < min_n."
                },
                "pay_n": {
                  "type": "integer",
                  "description": "Postings with pay at this level."
                }
              }
            }
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "role",
                "role_name",
                "postings"
              ],
              "properties": {
                "role": {
                  "type": "string",
                  "description": "Role key."
                },
                "role_name": {
                  "type": "string",
                  "description": "Display name."
                },
                "postings": {
                  "type": "integer",
                  "description": "Postings in the role that mention the skill."
                },
                "pct_of_role": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Percent of the role's postings."
                }
              }
            }
          }
        }
      },
      "Role": {
        "type": "object",
        "required": [
          "key",
          "name",
          "postings",
          "share_pct",
          "median_pay_usd",
          "pay_n"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Role family key."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "postings": {
            "type": "integer",
            "description": "Open postings in the role family."
          },
          "share_pct": {
            "type": "number",
            "description": "Percent of all tracked data & AI postings."
          },
          "median_pay_usd": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Median published US pay; null when n < min_n."
          },
          "pay_n": {
            "type": "integer",
            "description": "Postings with US USD pay."
          },
          "wow_change_postings": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Change in postings vs last week; null until two weeks of history exist."
          },
          "url": {
            "type": "string",
            "description": "Human page for the role."
          }
        }
      },
      "RoleDetail": {
        "type": "object",
        "required": [
          "key",
          "name",
          "postings",
          "share_pct",
          "median_pay_usd",
          "pay_n",
          "pay_by_level",
          "top_skills"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "Role family key."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "postings": {
            "type": "integer",
            "description": "Open postings in the role family."
          },
          "share_pct": {
            "type": "number",
            "description": "Percent of all tracked data & AI postings."
          },
          "median_pay_usd": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Median published US pay; null when n < min_n."
          },
          "pay_n": {
            "type": "integer",
            "description": "Postings with US USD pay."
          },
          "wow_change_postings": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Change in postings vs last week; null until two weeks of history exist."
          },
          "url": {
            "type": "string",
            "description": "Human page for the role."
          },
          "pay_by_level": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "level",
                "level_name",
                "median_pay_usd",
                "pay_n"
              ],
              "properties": {
                "level": {
                  "type": "string",
                  "description": "Level key from the job title (intern, entry, mid, senior, staff, principal, manager, director_plus, unspecified)."
                },
                "level_name": {
                  "type": "string",
                  "description": "Display name."
                },
                "median_pay_usd": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Median published US pay at this level; null when n < min_n."
                },
                "pay_n": {
                  "type": "integer",
                  "description": "Postings with pay at this level."
                }
              }
            }
          },
          "top_skills": {
            "type": "array",
            "description": "Most-requested skills in this role (up to 25).",
            "items": {
              "type": "object",
              "required": [
                "key",
                "name",
                "postings",
                "pct_of_role"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Skill key."
                },
                "name": {
                  "type": "string",
                  "description": "Display name."
                },
                "postings": {
                  "type": "integer",
                  "description": "Postings in the role that mention it."
                },
                "pct_of_role": {
                  "type": "number",
                  "description": "Percent of the role's postings."
                }
              }
            }
          }
        }
      },
      "SkillList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "count",
              "total",
              "data"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "description": "Items returned."
              },
              "total": {
                "type": "integer",
                "description": "Items matching before limit."
              },
              "filters": {
                "type": "object",
                "description": "Filters applied."
              },
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Skill"
                }
              }
            }
          }
        ]
      },
      "SkillResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "data": {
                "$ref": "#/components/schemas/SkillDetail"
              }
            }
          }
        ]
      },
      "RoleList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "count",
              "data"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "description": "Items returned."
              },
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Role"
                }
              }
            }
          }
        ]
      },
      "RoleResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "data": {
                "$ref": "#/components/schemas/RoleDetail"
              }
            }
          }
        ]
      },
      "LevelList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "count",
              "data"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "description": "Items returned."
              },
              "data": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "level",
                    "level_name",
                    "postings",
                    "median_pay_usd",
                    "pay_n"
                  ],
                  "properties": {
                    "level": {
                      "type": "string",
                      "description": "Level key."
                    },
                    "level_name": {
                      "type": "string",
                      "description": "Display name."
                    },
                    "postings": {
                      "type": "integer",
                      "description": "Postings at this level."
                    },
                    "median_pay_usd": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Median published US pay; null when n < min_n."
                    },
                    "pay_n": {
                      "type": "integer",
                      "description": "Postings with pay."
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "CategoryList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          },
          {
            "type": "object",
            "required": [
              "count",
              "data"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "description": "Items returned."
              },
              "data": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "category",
                    "skills"
                  ],
                  "properties": {
                    "category": {
                      "type": "string",
                      "description": "Category key."
                    },
                    "skills": {
                      "type": "integer",
                      "description": "Skills in the category."
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "Meta": {
        "type": "object",
        "required": [
          "name",
          "as_of",
          "week",
          "postings",
          "companies",
          "min_n",
          "license",
          "citation"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Dataset name."
          },
          "as_of": {
            "type": "string",
            "format": "date",
            "description": "Snapshot date."
          },
          "week": {
            "type": "string",
            "description": "ISO week."
          },
          "generated_at": {
            "type": "string",
            "description": "When the aggregates were computed (UTC)."
          },
          "first_snapshot": {
            "type": "string",
            "format": "date",
            "description": "First daily snapshot."
          },
          "postings": {
            "type": "integer",
            "description": "Open data & AI postings tracked."
          },
          "companies": {
            "type": "integer",
            "description": "Companies with postings."
          },
          "boards_total": {
            "type": "integer",
            "description": "Company job boards read."
          },
          "boards_tracked": {
            "type": "object",
            "description": "Boards per job-board vendor."
          },
          "with_us_usd_pay": {
            "type": "integer",
            "description": "Postings with US USD pay."
          },
          "min_n": {
            "type": "integer",
            "description": "Minimum postings with pay before a median is shown."
          },
          "skills": {
            "type": "integer",
            "description": "Skills tracked."
          },
          "roles": {
            "type": "integer",
            "description": "Role families."
          },
          "needs_more_history": {
            "type": "boolean",
            "description": "True until two weeks of history exist."
          },
          "pay_note": {
            "type": "string",
            "description": "How pay is measured."
          },
          "share_note": {
            "type": "string",
            "description": "How share is measured."
          },
          "license": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "description": "License."
              },
              "url": {
                "type": "string",
                "description": "License URL."
              }
            }
          },
          "citation": {
            "type": "string",
            "description": "Citation line."
          },
          "methodology": {
            "type": "string",
            "description": "Methodology page."
          },
          "csv": {
            "type": "string",
            "description": "CSV download."
          }
        }
      },
      "ApiIndex": {
        "type": "object",
        "required": [
          "name",
          "version",
          "as_of",
          "endpoints"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "API name."
          },
          "version": {
            "type": "string",
            "description": "API version."
          },
          "as_of": {
            "type": "string",
            "format": "date",
            "description": "Snapshot date."
          },
          "docs": {
            "type": "string",
            "description": "Docs page."
          },
          "openapi": {
            "type": "string",
            "description": "OpenAPI document."
          },
          "mcp": {
            "type": "string",
            "description": "MCP server endpoint (Streamable HTTP)."
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "path",
                "description"
              ],
              "properties": {
                "path": {
                  "type": "string",
                  "description": "Path."
                },
                "description": {
                  "type": "string",
                  "description": "What it returns."
                }
              }
            }
          }
        }
      }
    }
  }
}
