{
 "openapi": "3.1.0",
 "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
 "info": {
  "title": "Pristavla API",
  "version": "1.0.0",
  "summary": "Norwegian prices and what they consist of, as static JSON.",
  "description": "Read-only, free and without keys, for AI assistants, agents and individuals. The same numbers as the pages on pristavla.no and as the MCP server at https://pristavla.no/mcp, rebuilt every hour. Every page payload carries `license`, `attribution` and `cite`; cite the page URL. Numbers from MetalpriceAPI and CoinMarketCap, and rows from providers whose terms forbid reuse, are left out (see `notes` and `gated`). Content is in Norwegian. Limits per client IP (IPv6: per /64), shared with the MCP server: 60 requests a minute with bursts of 20 and 2 000 in 24 hours; over the limit the answer is 429 with `Retry-After`. Not for browsers on other websites: a request with an `Origin` header other than https://pristavla.no is refused (403) and there are no CORS headers. Using the API as the data source of another website, app or service, or bulk harvesting, needs a written agreement; see the terms (https://pristavla.no/bruk-tallene/#vilkar) or write to kontakt@datamynt.no. Norske priser og hva de består av, som JSON: de samme tallene som sidene, med lisens og kilde.",
  "termsOfService": "https://pristavla.no/bruk-tallene/#vilkar",
  "contact": {
   "name": "Datamynt AS",
   "email": "kontakt@datamynt.no",
   "url": "https://pristavla.no/bruk-tallene/"
  },
  "license": {
   "name": "CC BY 4.0 for Pristavla's own numbers; other sources keep their licences (per payload)",
   "identifier": "CC-BY-4.0"
  }
 },
 "servers": [
  {
   "url": "https://pristavla.no/api/v1",
   "description": "Static files, served by the rate-limited Pristavla server (no CORS)"
  }
 ],
 "externalDocs": {
  "url": "https://pristavla.no/bruk-tallene/#api",
  "description": "Bruk tallene: API, MCP, vilkår og lisens"
 },
 "tags": [
  {
   "name": "pages"
  },
  {
   "name": "series"
  }
 ],
 "paths": {
  "/index.json": {
   "get": {
    "operationId": "getIndex",
    "tags": [
     "pages"
    ],
    "summary": "Every page and every dataset",
    "responses": {
     "200": {
      "description": "The index",
      "headers": {
       "ETag": {
        "schema": {
         "type": "string"
        }
       },
       "Last-Modified": {
        "schema": {
         "type": "string"
        }
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Index"
        }
       }
      }
     },
     "304": {
      "description": "Not modified (If-None-Match / If-Modified-Since)."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "description": "No such page or dataset."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     }
    }
   }
  },
  "/pages/{slug}.json": {
   "get": {
    "operationId": "getPage",
    "tags": [
     "pages"
    ],
    "summary": "One page's numbers, as the page shows them",
    "description": "`slug` is the page's path without slashes at the ends; nested pages keep their inner slashes (`kommunale-avgifter/oslo`, `valuta/eur`). The front page is `forside`. Find slugs in /index.json.",
    "parameters": [
     {
      "name": "slug",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[a-z0-9-]+(/[a-z0-9-]+)*$"
      },
      "examples": {
       "diesel": {
        "value": "diesel"
       },
       "kommune": {
        "value": "kommunale-avgifter/oslo"
       }
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The page payload",
      "headers": {
       "ETag": {
        "schema": {
         "type": "string"
        }
       },
       "Last-Modified": {
        "schema": {
         "type": "string"
        }
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Page"
        }
       }
      }
     },
     "304": {
      "description": "Not modified (If-None-Match / If-Modified-Since)."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "description": "No such page or dataset."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     }
    }
   }
  },
  "/series/{dataset}.json": {
   "get": {
    "operationId": "getSeries",
    "tags": [
     "series"
    ],
    "summary": "One table from /data/, every row",
    "parameters": [
     {
      "name": "dataset",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "enum": [
        "aluminium-eksport-ssb",
        "boliglansrente-ssb",
        "boligpriser-ssb",
        "drivstoff-europa",
        "drivstoff-oppdeling",
        "drivstoff-ssb",
        "kommunale-avgifter-ssb",
        "laks-eksportpris-ssb",
        "matpriser-kpi-ssb",
        "olje-gass-eia-dag",
        "olje-gass-eia-maned",
        "ravarer-verdensbanken",
        "renter-norges-bank",
        "strompris-husholdninger-ssb",
        "styringsrente-endringer",
        "tobakksavgift",
        "valuta-dag",
        "valuta-maned"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The table, with licence and sources",
      "headers": {
       "ETag": {
        "schema": {
         "type": "string"
        }
       },
       "Last-Modified": {
        "schema": {
         "type": "string"
        }
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Series"
        }
       }
      }
     },
     "304": {
      "description": "Not modified (If-None-Match / If-Modified-Since)."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "description": "No such page or dataset."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     }
    }
   }
  },
  "/search.json": {
   "get": {
    "operationId": "getSearchIndex",
    "tags": [
     "pages"
    ],
    "summary": "The site search index",
    "responses": {
     "200": {
      "description": "The search index",
      "headers": {
       "ETag": {
        "schema": {
         "type": "string"
        }
       },
       "Last-Modified": {
        "schema": {
         "type": "string"
        }
       },
       "RateLimit": {
        "$ref": "#/components/headers/RateLimit"
       },
       "RateLimit-Policy": {
        "$ref": "#/components/headers/RateLimitPolicy"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/SearchIndex"
        }
       }
      }
     },
     "304": {
      "description": "Not modified (If-None-Match / If-Modified-Since)."
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "404": {
      "description": "No such page or dataset."
     },
     "429": {
      "$ref": "#/components/responses/TooManyRequests"
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Index": {
    "type": "object",
    "required": [
     "pages",
     "datasets",
     "version"
    ],
    "properties": {
     "format": {
      "type": "integer"
     },
     "site": {
      "type": "string"
     },
     "updated": {
      "type": [
       "string",
       "null"
      ]
     },
     "version": {
      "type": "string"
     },
     "pages": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "slug": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "title": {
         "type": "string"
        },
        "url": {
         "type": "string"
        },
        "kind": {
         "type": [
          "string",
          "null"
         ]
        },
        "updated": {
         "type": [
          "string",
          "null"
         ]
        },
        "headline": {
         "type": [
          "string",
          "null"
         ]
        },
        "rows": {
         "type": "integer"
        },
        "gated": {
         "type": "boolean"
        }
       },
       "required": [
        "slug",
        "title",
        "url"
       ]
      }
     },
     "datasets": {
      "type": "array",
      "items": {
       "type": "object",
       "required": [
        "slug",
        "title"
       ],
       "properties": {
        "slug": {
         "type": "string"
        },
        "title": {
         "type": "string"
        },
        "group": {
         "type": "string"
        },
        "kind": {
         "type": "string"
        },
        "updated": {
         "type": "string"
        },
        "coverage": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "columns": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "rows": {
         "type": "integer"
        },
        "license": {
         "type": "string"
        },
        "page": {
         "type": "string"
        }
       }
      }
     },
     "policy": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      }
     }
    }
   },
   "Page": {
    "type": "object",
    "properties": {
     "slug": {
      "type": "string"
     },
     "url": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "title": {
      "type": "string"
     },
     "kind": {
      "type": [
       "string",
       "null"
      ]
     },
     "summary": {
      "type": [
       "string",
       "null"
      ]
     },
     "updated": {
      "type": [
       "string",
       "null"
      ]
     },
     "checked": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "from": {
        "type": "string"
       },
       "to": {
        "type": "string"
       }
      }
     },
     "hero": {
      "type": [
       "object",
       "null"
      ],
      "description": "Sidens hovedtall slik det står øverst på siden. The page's headline number as printed.",
      "properties": {
       "label": {
        "type": [
         "string",
         "null"
        ]
       },
       "value": {
        "type": "string"
       },
       "unit": {
        "type": "string"
       },
       "number": {
        "type": [
         "number",
         "null"
        ]
       },
       "stamp": {
        "type": [
         "string",
         "null"
        ]
       },
       "note": {
        "type": [
         "string",
         "null"
        ]
       },
       "text": {
        "type": [
         "string",
         "null"
        ]
       },
       "from": {
        "type": "string",
        "enum": [
         "hero",
         "share_card"
        ]
       }
      },
      "required": [
       "value",
       "unit",
       "number"
      ]
     },
     "rows": {
      "type": [
       "array",
       "null"
      ],
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "value": {
         "type": [
          "number",
          "string",
          "null"
         ]
        },
        "unit": {
         "type": "string"
        },
        "details": {
         "type": "object",
         "additionalProperties": {
          "type": [
           "string",
           "number",
           "boolean",
           "null"
          ]
         }
        },
        "source_url": {
         "type": [
          "string",
          "null"
         ]
        },
        "checked": {
         "type": [
          "string",
          "null"
         ]
        },
        "license": {
         "type": "string",
         "enum": [
          "facts",
          "cc-by-4.0",
          "nlod-2.0",
          "public-domain",
          "lov"
         ],
         "description": "facts = read from the provider's own page, no licence given; lov = statutory (åndsverkloven § 14); otherwise the source's licence"
        }
       },
       "required": [
        "name",
        "value",
        "unit",
        "source_url",
        "checked",
        "license"
       ]
      }
     },
     "datasets": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "slug": {
         "type": "string"
        },
        "title": {
         "type": "string"
        }
       }
      }
     },
     "gated": {
      "type": "boolean"
     },
     "notes": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "license": {
      "anyOf": [
       {
        "type": "object",
        "required": [
         "name",
         "url"
        ],
        "properties": {
         "name": {
          "type": "string"
         },
         "url": {
          "type": "string"
         }
        }
       },
       {
        "type": "string",
        "enum": [
         "facts"
        ]
       },
       {
        "type": "null"
       }
      ],
      "description": "«facts»: priser lest fra tilbyderens egen side, ingen lisens gis. Prices read from the provider's own page; facts, no licence given."
     },
     "license_scope": {
      "type": "string"
     },
     "attribution": {
      "type": [
       "string",
       "null"
      ]
     },
     "cite": {
      "type": "string"
     },
     "format": {
      "type": "integer"
     }
    },
    "required": [
     "slug",
     "url",
     "title",
     "hero",
     "rows",
     "gated",
     "notes",
     "license",
     "attribution",
     "cite"
    ]
   },
   "Series": {
    "type": "object",
    "required": [
     "slug",
     "title",
     "columns",
     "rows",
     "license",
     "attribution"
    ],
    "properties": {
     "title": {
      "type": "string"
     },
     "description": {
      "type": "string"
     },
     "page": {
      "type": "string"
     },
     "csv": {
      "type": "string"
     },
     "json": {
      "type": "string"
     },
     "updated": {
      "type": "string"
     },
     "coverage": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "kind": {
      "type": "string"
     },
     "license": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "name": {
        "type": "string"
       },
       "url": {
        "type": "string"
       }
      }
     },
     "license_scope": {
      "type": "string"
     },
     "attribution": {
      "type": "string"
     },
     "sources": {
      "type": "array",
      "items": {
       "type": "object"
      }
     },
     "notes": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "columns": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "description": {
         "type": "string"
        }
       }
      }
     },
     "rows": {
      "type": "array",
      "items": {
       "type": "array"
      }
     },
     "slug": {
      "type": "string"
     },
     "period_column": {
      "type": "string"
     },
     "group": {
      "type": "string"
     }
    }
   },
   "SearchIndex": {
    "type": "object",
    "required": [
     "k",
     "p"
    ],
    "description": "The site search index (the same as /sok.json): k = [[kind label, path prefix, words]], p = [[title, slug tail, kind index, words?]]; a page's path is k[kind][1] + tail. The search rules are in static/nav.js; the MCP search tool runs them for you.",
    "properties": {
     "k": {
      "type": "array",
      "items": {
       "type": "array"
      }
     },
     "p": {
      "type": "array",
      "items": {
       "type": "array"
      }
     }
    }
   },
   "Error": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "object",
      "required": [
       "status",
       "message"
      ],
      "properties": {
       "status": {
        "type": "integer"
       },
       "message": {
        "type": "string"
       },
       "terms": {
        "type": "string",
        "format": "uri",
        "description": "The terms of use."
       }
      }
     }
    }
   }
  },
  "headers": {
   "RateLimit": {
    "description": "Quota left per policy (draft-ietf-httpapi-ratelimit-headers), e.g. `\"minute\";r=19;t=1, \"day\";r=1999;t=86400`: r = requests left, t = seconds until it resets.",
    "schema": {
     "type": "string"
    }
   },
   "RateLimitPolicy": {
    "description": "The quotas (draft-ietf-httpapi-ratelimit-headers): `\"minute\";q=60;w=60, \"day\";q=2000;w=86400`.",
    "schema": {
     "type": "string"
    }
   },
   "RetryAfter": {
    "description": "Seconds to wait.",
    "schema": {
     "type": "integer"
    }
   }
  },
  "responses": {
   "Forbidden": {
    "description": "The request carried an `Origin` header other than https://pristavla.no (a browser on another website) or an unknown `Host`. The body says why and links the terms.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "TooManyRequests": {
    "description": "Over the per-IP limit (60 a minute, bursts of 20, 2 000 in 24 hours). Wait `Retry-After` seconds.",
    "headers": {
     "Retry-After": {
      "$ref": "#/components/headers/RetryAfter"
     },
     "RateLimit": {
      "$ref": "#/components/headers/RateLimit"
     },
     "RateLimit-Policy": {
      "$ref": "#/components/headers/RateLimitPolicy"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  }
 }
}
