{
 "subscribe": "Free shutdown alerts + AI model migration checklist: https://aideprecations.com/checklist/ (agents: MCP subscribe_alerts at https://aideprecations.com/mcp or POST https://aideprecations.com/api/subscribe)",
 "openapi": "3.1.0",
 "info": {
  "title": "AI Deprecations API",
  "version": "1.2.0",
  "description": "Read-only static JSON API of AI model deprecation and shutdown dates, normalized from official provider pages. Every record has source_url and last_checked. Data CC BY 4.0; cite AI Deprecations with a link.",
  "license": {
   "name": "CC BY 4.0",
   "url": "https://creativecommons.org/licenses/by/4.0/"
  },
  "contact": {
   "name": "AI Deprecations (API)",
   "email": "modelsunset@mail.grokbot.com",
   "url": "https://aideprecations.com/developers/"
  },
  "x-subscribe": "https://aideprecations.com/subscribe/"
 },
 "servers": [
  {
   "url": "https://aideprecations.com",
   "description": "AI Deprecations (static JSON + API on aideprecations.com)"
  }
 ],
 "paths": {
  "/api/v1/models.json": {
   "get": {
    "operationId": "listModels",
    "summary": "All tracked models",
    "responses": {
     "200": {
      "description": "All models",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "models": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Model"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/providers/{provider}.json": {
   "get": {
    "operationId": "getProvider",
    "summary": "Models for one provider",
    "parameters": [
     {
      "name": "provider",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "enum": [
        "openai",
        "anthropic",
        "google-gemini",
        "google-vertex",
        "azure-openai",
        "aws-bedrock",
        "mistral",
        "cohere"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Provider models",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "models": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Model"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/models/{provider}/{slug}.json": {
   "get": {
    "operationId": "getModel",
    "summary": "One model",
    "parameters": [
     {
      "name": "provider",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "slug",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Model",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "model": {
           "$ref": "#/components/schemas/Model"
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/upcoming/{days}.json": {
   "get": {
    "operationId": "listUpcoming",
    "summary": "Shutdowns in the next N days",
    "parameters": [
     {
      "name": "days",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer",
       "enum": [
        7,
        30,
        90,
        365
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Upcoming shutdowns",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "models": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Model"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/changes": {
   "servers": [
    {
     "url": "https://aideprecations.com/api",
     "description": "Live subscription endpoints on aideprecations.com"
    }
   ],
   "get": {
    "operationId": "getChangesSince",
    "summary": "Records changed since a time",
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "provider",
      "in": "query",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "since, tracking_since, checked_at, count, changes[], and warning when since is earlier than tracking_since (history before tracking_since is not recorded). Poll with since = previous checked_at. changes[] (each a Model plus changed_at and change{type,fields})"
     }
    }
   }
  },
  "/subscribe": {
   "servers": [
    {
     "url": "https://aideprecations.com/api",
     "description": "Live subscription endpoints on aideprecations.com"
    }
   ],
   "post": {
    "operationId": "subscribe",
    "summary": "Subscribe a webhook (JSON POST on changes, checked daily) or an email (double opt-in newsletter; personal .ics calendar feed is issued only after the email is confirmed)",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "webhook": {
          "type": "string",
          "format": "uri",
          "description": "public https URL"
         },
         "email": {
          "type": "string",
          "format": "email"
         },
         "providers": {
          "type": "array",
          "items": {
           "type": "string",
           "enum": [
            "openai",
            "anthropic",
            "google-gemini",
            "google-vertex",
            "azure-openai",
            "aws-bedrock",
            "mistral",
            "cohere"
           ]
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok, status, unsubscribe_token (webhooks)"
     },
     "400": {
      "description": "invalid input"
     }
    }
   }
  },
  "/unsubscribe": {
   "servers": [
    {
     "url": "https://aideprecations.com/api",
     "description": "Live subscription endpoints on aideprecations.com"
    }
   ],
   "post": {
    "operationId": "unsubscribe",
    "summary": "Stop webhook alerts",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "token": {
          "type": "string"
         }
        },
        "required": [
         "token"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "ok, status"
     }
    }
   }
  },
  "/api/v1/coverage.json": {
   "get": {
    "operationId": "getCoverage",
    "summary": "Per-provider rollup today: known_date, vendor_gave_no_date, rows_not_verified_today, source_pages_unreadable_today",
    "description": "Per provider, today: known_date = rows with a shutdown date printed on the official page; vendor_gave_no_date = rows read successfully today where the page gives no date; rows_not_verified_today = rows carried forward because the page could not be read; source_pages_unreadable_today = official pages that failed to fetch or parse on the latest run.",
    "responses": {
     "200": {
      "description": "providers[]",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "providers": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/ProviderRollup"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/source-reviews.json": {
   "get": {
    "operationId": "getSourceReviews",
    "summary": "Manual reviews of unreadable source pages (separate from automatic checks)",
    "description": "source_reviews = separate log of a person manually checking a source page the parser could not read (reviewed_at, source_url, provider, finding). It is never auto-filled; an empty list means no manual review has been recorded.",
    "responses": {
     "200": {
      "description": "reviews[]",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "reviews": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/SourceReview"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Model": {
    "type": "object",
    "properties": {
     "provider": {
      "type": [
       "string",
       "null"
      ]
     },
     "provider_name": {
      "type": [
       "string",
       "null"
      ]
     },
     "model_id": {
      "type": [
       "string",
       "null"
      ]
     },
     "slug": {
      "type": [
       "string",
       "null"
      ]
     },
     "status": {
      "type": [
       "string",
       "null"
      ]
     },
     "announced": {
      "type": [
       "string",
       "null"
      ]
     },
     "shutdown": {
      "type": [
       "string",
       "null"
      ]
     },
     "shutdown_note": {
      "type": [
       "string",
       "null"
      ]
     },
     "replacement": {
      "type": [
       "string",
       "null"
      ],
      "description": "Provider's own replacement wording, verbatim (may list several models joined by ' or ' or commas)."
     },
     "replacement_status": {
      "type": [
       "string",
       "null"
      ],
      "description": "Status of the resolved replacement. Resolution rule: split replacement on ' or ', commas and whitespace; keep tokens that exactly match a model_id in the SAME provider; pick the best status (active, then deprecated, then retired). No cross-provider lookup."
     },
     "replacement_shutdown": {
      "type": [
       "string",
       "null"
      ]
     },
     "replacement_final": {
      "type": [
       "string",
       "null"
      ],
      "description": "End of the replacement chain within the same provider: the first active model reached, or, if none, the deepest tracked model. Null means the pointer leaves our data."
     },
     "replacement_final_status": {
      "type": [
       "string",
       "null"
      ],
      "description": "Status of replacement_final (active / deprecated / retired)."
     },
     "replacement_provider": {
      "type": [
       "string",
       "null"
      ],
      "description": "Provider of the resolved replacement (always the row's own provider)."
     },
     "replacement_final_provider": {
      "type": [
       "string",
       "null"
      ],
      "description": "Provider of replacement_final."
     },
     "replacement_page": {
      "type": [
       "string",
       "null"
      ],
      "description": "page of the resolved replacement row. page is the unique row key in this file (model_id is not unique, even within a provider). Tie-break when several rows match: best status, then the order the provider lists the models in, then (for duplicate model_id rows) no shutdown or the latest shutdown first, then page."
     },
     "replacement_final_page": {
      "type": [
       "string",
       "null"
      ],
      "description": "page of the replacement_final row (unique key; join on this, not on model_id)."
     },
     "replacement_kind": {
      "type": [
       "string",
       "null"
      ],
      "description": "replacement_kind compares the row's model_id with the provider's replacement wording. Family = model_id lowercased, provider prefix (e.g. 'anthropic.') and ':version' suffix removed, date stamps removed, then the leading name before the first version number (e.g. gpt-4o-mini-2024-07-18 -> 'gpt', claude-3-5-sonnet -> 'claude', mistral-large-2407 -> 'mistral-large'). 'same_family' = exactly one model-id-like token in the replacement and its family equals the row's family; 'different_family' = one token, different family; 'multiple_options' = the provider names 2+ models. null when the rule cannot place the row; replacement_kind_reason then says why ('no_replacement_listed' or 'replacement_not_a_model_id').",
      "enum": [
       "same_family",
       "different_family",
       "multiple_options",
       null
      ]
     },
     "replacement_kind_reason": {
      "type": [
       "string",
       "null"
      ],
      "description": "Why replacement_kind is null: no_replacement_listed or replacement_not_a_model_id. Null when replacement_kind is set."
     },
     "source_url": {
      "type": [
       "string",
       "null"
      ]
     },
     "last_checked": {
      "type": [
       "string",
       "null"
      ]
     },
     "last_attempt": {
      "type": [
       "string",
       "null"
      ],
      "description": "When we last tried to read source_url for this row."
     },
     "check_status": {
      "type": [
       "string",
       "null"
      ],
      "description": "verified = last attempt read the row from source_url; not_verified = last attempt failed, row carried forward unchanged (see data.json sources[] for error_class)."
     },
     "page": {
      "type": [
       "string",
       "null"
      ]
     },
     "api": {
      "type": [
       "string",
       "null"
      ]
     },
     "markdown": {
      "type": [
       "string",
       "null"
      ]
     }
    }
   },
   "ProviderRollup": {
    "type": "object",
    "properties": {
     "provider": {
      "type": "string"
     },
     "provider_name": {
      "type": "string"
     },
     "rows": {
      "type": "integer"
     },
     "known_date": {
      "type": "integer"
     },
     "vendor_gave_no_date": {
      "type": "integer"
     },
     "rows_not_verified_today": {
      "type": "integer"
     },
     "source_pages": {
      "type": "integer"
     },
     "source_pages_unreadable_today": {
      "type": "integer"
     },
     "unreadable_source_urls": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "as_of": {
      "type": "string",
      "format": "date"
     }
    }
   },
   "SourceReview": {
    "type": "object",
    "properties": {
     "reviewed_at": {
      "type": "string",
      "format": "date-time"
     },
     "provider": {
      "type": "string"
     },
     "source_url": {
      "type": "string"
     },
     "reviewer": {
      "type": "string",
      "description": "person, not an automated check"
     },
     "finding": {
      "type": "string"
     }
    }
   }
  }
 }
}