openapi: 3.1.0
info:
  title: ProteinBenchmark API
  version: "1.0.0"
  description: >
    Read-only JSON API over the ProteinBenchmark product dataset (protein
    powders, snacks, restaurant items, whole foods, restaurant-dish estimates).
    Computed metrics — Protein Density, tier, DAY (bioavailability-adjusted
    yield), and PCE (protein cost-efficiency) — ship inside each product so
    agents need not reimplement the math. The git-tracked Astro content
    collections are the source of truth; this API is a one-way mirror that
    refreshes on each site deploy.
  contact:
    name: ProteinBenchmark
    email: info@proteinbenchmark.com
    url: https://proteinbenchmark.com
  license:
    name: Free to cite with attribution to proteinbenchmark.com
    url: https://proteinbenchmark.com/api
servers:
  - url: https://proteinbenchmark.com/api/v1
    description: Production
security: []
tags:
  - name: products
  - name: reference
paths:
  /products:
    get:
      operationId: listProducts
      summary: List / filter products
      tags: [products]
      description: >
        Returns products filtered by the query parameters below, sorted
        descending by `sort` (default `density`), with offset pagination via
        `cursor`. Responses are CDN-cached for a day.
      parameters:
        - { name: category, in: query, schema: { type: string, enum: [snack, powder, restaurant, food, dish] } }
        - { name: protein_source, in: query, schema: { type: string, enum: [whey, dairy, egg, meat, soy, pea, mixed, grain, collagen] } }
        - { name: sweetener_type, in: query, schema: { type: string } }
        - { name: has_cert, in: query, description: "Cert slug; matches products whose third_party_certs contains it.", schema: { type: string } }
        - { name: excludes_sugar_alcohols, in: query, description: "true → only products with no sugar alcohols.", schema: { type: string, enum: ["true"] } }
        - { name: heavy_metal_tested, in: query, schema: { type: string, enum: ["true"] } }
        - { name: min_density, in: query, schema: { type: number } }
        - { name: min_pce, in: query, schema: { type: number } }
        - { name: min_day, in: query, schema: { type: number } }
        - { name: sort, in: query, schema: { type: string, enum: [density, pce, day, leucine_estimate_g, protein_g], default: density } }
        - { name: limit, in: query, schema: { type: integer, default: 50, maximum: 200, minimum: 1 } }
        - { name: cursor, in: query, description: "Offset cursor from a prior response's next_cursor.", schema: { type: string } }
      responses:
        "200":
          description: A page of products.
          content:
            application/json:
              schema:
                type: object
                properties:
                  items: { type: array, items: { $ref: "#/components/schemas/Product" } }
                  count: { type: integer }
                  total: { type: integer }
                  limit: { type: integer }
                  next_cursor: { type: [string, "null"] }
                  sort: { type: string }
        "400":
          description: Invalid query parameter.
  /products/{id}:
    get:
      operationId: getProduct
      summary: Get a single product by its namespaced id
      tags: [products]
      parameters:
        - { name: id, in: path, required: true, schema: { type: string }, description: "e.g. powder-dymatize-iso100-hydrolyzed" }
      responses:
        "200":
          description: The product.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Product" }
        "404":
          description: No product with that id.
  /coefficients:
    get:
      operationId: getCoefficients
      summary: DIAAS coefficients, tier thresholds, and certification metadata
      tags: [reference]
      responses:
        "200":
          description: Reference data so agents can recompute or explain the scoring.
        "404":
          description: Not found.
  /methodology:
    get:
      operationId: getMethodology
      summary: Machine-readable formula definitions (density, PCE, DAY)
      tags: [reference]
      responses:
        "200":
          description: Formulas + a link to the human-readable /methodology page.
        "404":
          description: Not found.
components:
  schemas:
    Product:
      type: object
      description: >
        A scored product. The authoritative JSON Schema (with all enums) is at
        https://proteinbenchmark.com/.well-known/products-schema.json — this is a
        usable summary. Additional category-specific fields may also be present.
      required: [id, slug, category, name, calories, protein_g, density, tier]
      additionalProperties: true
      properties:
        id: { type: string, description: "Namespaced unique id, e.g. powder-dymatize-iso100-hydrolyzed." }
        slug: { type: string }
        category: { type: string, enum: [snack, powder, restaurant, food, dish] }
        name: { type: string }
        brand: { type: string }
        serving_size_g: { type: [number, "null"] }
        calories: { type: number }
        protein_g: { type: number }
        protein_source: { type: [string, "null"], enum: [whey, dairy, egg, meat, soy, pea, mixed, grain, collagen, null] }
        leucine_g: { type: [number, "null"] }
        sweetener_type: { type: [string, "null"] }
        sugar_alcohols: { type: array, items: { type: string } }
        third_party_certs: { type: array, items: { type: string } }
        heavy_metal_tested: { type: [boolean, "null"] }
        density: { type: number }
        tier: { type: string, enum: [platinum, gold, silver, avoid, unknown] }
        g_per_100kcal: { type: number }
        pce: { type: [number, "null"] }
        day: { type: [number, "null"] }
        leucine_estimate_g: { type: [number, "null"] }
        meets_leucine_threshold: { type: [boolean, "null"] }
        last_synced_at: { type: string, format: date-time }
        source_commit_sha: { type: string }
        source_collection: { type: string }
