openapi: 3.1.0
info:
  title: Modelmeter API
  version: 0.1.0
  description: |
    The changelog of record for the AI stack: source-verified events covering what
    changes in the model layer — deprecations with sunset dates and migration
    targets, price changes, releases — plus current pricing, model cards, and
    three years of price history.

    All responses are JSON. URLs are stable. Endpoints do not require authentication for V1,
    but rate limits apply per IP. An API-key tier with higher limits is planned.

    The data backing this API lives at https://github.com/modelmeters/modelmeter and is
    updated by an autonomous watch/draft pipeline plus human review. Every model entry
    includes a `last_verified` field and every event a verification `status`; judge
    freshness and confidence from those, not from vibes.
  contact:
    name: Modelmeter
    url: https://modelmeter.xyz
  license:
    name: MIT
servers:
  - url: https://modelmeter.xyz
paths:
  /estimate:
    get:
      summary: Estimate the cost of an LLM call.
      description: |
        Returns the cost of a hypothetical call with the given input and output token counts
        against the named model. Math is shown in the response so agents can verify.
      parameters:
        - name: model
          in: query
          required: true
          description: Model id in `provider/model` form, e.g. `anthropic/claude-sonnet-4-6`.
          schema: { type: string }
        - name: input
          in: query
          required: true
          description: Input token count.
          schema: { type: integer, minimum: 0 }
        - name: output
          in: query
          required: true
          description: Output token count.
          schema: { type: integer, minimum: 0 }
        - name: format
          in: query
          required: false
          description: Response format. Default is `json`.
          schema: { type: string, enum: [json], default: json }
      responses:
        "200":
          description: Successful estimate.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Estimate" }
        "400":
          description: Invalid query parameters.
        "404":
          description: Model not found or unverified.
  /models:
    get:
      summary: List available models with their current pricing.
      responses:
        "200":
          description: Array of models.
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Model" }
  /model:
    get:
      summary: Unified card for a single model.
      description: |
        Returns one model normalized into a single object: pricing, capabilities
        (context window, vision, reasoning, tags), availability, reseller markup vs.
        the upstream entry, and a price-history summary (launch vs. current price,
        percent change, last change date, all-time low/high). Saves an agent the
        joins it would otherwise do across /models, /pricing.json, and /history.
      parameters:
        - name: id
          in: query
          required: true
          description: Model id in `provider/model` form, e.g. `anthropic/claude-opus-4-8`. Alias `model` also accepted.
          schema: { type: string }
      responses:
        "200":
          description: Unified model card.
          content:
            application/json:
              schema: { type: object }
        "400":
          description: Missing `id` parameter.
        "404":
          description: Model not found.
  /check:
    get:
      summary: Is my stack okay?
      description: |
        The record's flagship question. For each model id: scheduled retirements
        with days-remaining and migration targets, past retirements, other
        breaking/action-required changes, or a clean bill. Id matching tolerates
        dots vs dashes, dated snapshot suffixes, and bare or provider-prefixed
        forms. Also available as the MCP tool `check_model_dependencies` and the
        GitHub Action `modelmeters/modelmeter/actions/check`.
      parameters:
        - name: models
          in: query
          required: true
          description: Comma-separated model ids, max 50 (e.g. `gpt-4o,claude-sonnet-4-6`).
          schema: { type: string }
      responses:
        "200":
          description: Verdict per model plus a summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  checked_at: { type: string, format: date }
                  summary: { type: object }
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        query: { type: string }
                        model_id: { type: string, description: "Resolved catalog id, null if unknown." }
                        status: { type: string, enum: [ok, scheduled, retired, affected, deprecated_in_catalog, unknown] }
                        days_remaining: { type: integer }
                        effective_at: { type: string, format: date }
                        migration_target: { type: string }
                        events: { type: array, items: { type: object } }
        "400":
          description: Missing or invalid models parameter.
  /deprecations:
    get:
      summary: Per-model retirement rows — which model dies when.
      description: |
        The events record expanded to one row per affected model, with runway
        (days_remaining), migration target, source, and verification status.
      parameters:
        - name: provider
          in: query
          required: false
          schema: { type: string }
        - name: model
          in: query
          required: false
          schema: { type: string }
        - name: status
          in: query
          required: false
          schema: { type: string, enum: [scheduled, retired, all], default: scheduled }
        - name: limit
          in: query
          required: false
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
      responses:
        "200":
          description: Deprecation rows sorted by shutdown date.
          content:
            application/json:
              schema: { type: object }
  /feed.json:
    get:
      summary: JSON Feed 1.1 of operational events (breaking + action_required). RSS twin at /feed.xml.
      responses:
        "200":
          description: JSON Feed.
          content:
            application/feed+json:
              schema: { type: object }
  /events:
    get:
      summary: The changelog of record for the model layer.
      description: |
        Source-verified events covering what changed at AI providers — deprecations,
        price changes, launches — plus the market events (funding, partnerships,
        legal, regulatory) around them. Each event carries a `severity` describing
        what it demands of consumers of the affected models, `announced_at` /
        `effective_at` dates, at least one source, and a verification `status`.
        Newest first.
      parameters:
        - name: provider
          in: query
          required: false
          description: Filter to events touching this provider id.
          schema: { type: string }
        - name: type
          in: query
          required: false
          description: Event type, e.g. `model_deprecation`, `pricing_change`, `model_launch`.
          schema: { type: string }
        - name: model
          in: query
          required: false
          description: Filter to events affecting this model id (`provider/model` form).
          schema: { type: string }
        - name: severity
          in: query
          required: false
          schema: { type: string, enum: [breaking, action_required, informational] }
        - name: status
          in: query
          required: false
          description: Verification status. Default `verified`. `all` returns verified + unverified; `corrected` (superseded) entries are only returned when requested explicitly.
          schema: { type: string, enum: [verified, unverified, all, corrected], default: verified }
        - name: since
          in: query
          required: false
          description: ISO date; events announced on/after.
          schema: { type: string, format: date }
        - name: until
          in: query
          required: false
          description: ISO date; events announced on/before.
          schema: { type: string, format: date }
        - name: limit
          in: query
          required: false
          schema: { type: integer, minimum: 1, maximum: 500, default: 200 }
      responses:
        "200":
          description: Matching events, newest first.
          content:
            application/json:
              schema:
                type: object
                properties:
                  schema_version: { type: string }
                  snapshot_date: { type: string, format: date }
                  count: { type: integer }
                  events:
                    type: array
                    items: { $ref: "#/components/schemas/Event" }
  /events.json:
    get:
      summary: Raw events snapshot, identical to the repo's `events/current.json` (schema `events/schema.json`).
      responses:
        "200":
          description: Events snapshot.
          content:
            application/json:
              schema: { type: object }
  /pricing.json:
    get:
      summary: Raw current pricing snapshot, identical to the repo's `pricing/current.json`.
      responses:
        "200":
          description: Pricing snapshot.
          content:
            application/json:
              schema: { type: object }
components:
  schemas:
    Model:
      type: object
      properties:
        id: { type: string }
        provider: { type: string }
        display_name: { type: string }
        input_cost_per_mtok: { type: number, nullable: true }
        output_cost_per_mtok: { type: number, nullable: true }
        context_window: { type: integer, nullable: true }
        last_verified: { type: string, format: date }
    Event:
      type: object
      description: Full field definitions live in the versioned JSON Schema at `events/schema.json` (id https://modelmeter.xyz/schema/events/2.0.0).
      properties:
        id: { type: string }
        announced_at: { type: string, format: date, description: "Date the change became public." }
        effective_at: { type: string, format: date, description: "Date the change takes effect (e.g. sunset date). Absent when immediate or N/A." }
        detected_at: { type: string, format: date-time, description: "When the pipeline first detected the change. Absent on curated/backfilled entries." }
        type: { type: string }
        severity: { type: string, enum: [breaking, action_required, informational] }
        status: { type: string, enum: [unverified, verified, corrected] }
        providers: { type: array, items: { type: string } }
        models: { type: array, items: { type: string } }
        migration_target: { type: string, description: "Provider-recommended replacement model id, for deprecations/renames/swaps." }
        corrects: { type: string, description: "For correction events, the id of the superseded event." }
        headline: { type: string }
        summary: { type: string }
        sources:
          type: array
          items:
            type: object
            properties:
              url: { type: string, format: uri }
              quote: { type: string, description: "Verbatim excerpt from the source at detection time." }
              archived_url: { type: string, format: uri }
        tags: { type: array, items: { type: string } }
        created_at: { type: string, format: date }
        updated_at: { type: string, format: date }
    Estimate:
      type: object
      properties:
        model: { type: string }
        input_tokens: { type: integer }
        output_tokens: { type: integer }
        input_cost_usd: { type: number }
        output_cost_usd: { type: number }
        total_cost_usd: { type: number }
        pricing_date: { type: string, format: date, description: "`last_verified` of the model entry used." }
        source_url: { type: string, format: uri }