---
title: "Deal and pricing changes"
method: GET
path: "/api/changes"
---

# Deal and pricing changes

`GET /api/changes`

## Request

- Base URL: `https://agentdeals.dev`
- URL: `https://agentdeals.dev/api/changes`
- Auth: none required

Returns tracked pricing and tier changes across vendors. Filter by date, change type, vendor, or category. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page. The rule and the four totals it separates are at /changes#what-counts, and every response carries them as change_census.

## Query parameters

- `since` string, date
- `type` 'free_tier_removed' | 'open_source_killed' | 'product_deprecated' | 'limits_reduced' | 'restriction' | 'pricing_restructured' | 'pricing_model_change' | 'limits_increased' | 'new_free_tier' | 'new_tier' | 'startup_program_expanded' | 'pricing_postponed' | 'rebranded' | 'record_corrected'
- `vendor` string
- `vendors` string
- `category` string
- `categories` string
- `limit` integer
- `offset` integer
- `include_retracted` boolean
- `include_index_housekeeping` boolean

## Response `200`

List of deal changes

- object
  - `changes` PublishedDealChange[]
    - `vendor` string, required
    - `change_type` 'free_tier_removed' | 'open_source_killed' | 'product_deprecated' | 'limits_reduced' | 'restriction' | 'pricing_restructured' | 'pricing_model_change' | 'limits_increased' | 'new_free_tier' | 'new_tier' | 'startup_program_expanded' | 'pricing_postponed' | 'rebranded' | 'record_corrected', required
    - `reports` 'vendor_offer' | 'our_index': What the record is about. 'vendor_offer' — the vendor changed its own offer, and a vendor page is where that can be read. 'our_index' — we changed what we list while the vendor's offer stood still, so no vendor page evidences it and the record carries no source_url. Absent means vendor_offer. Filter these out of any figure you present as vendor market activity.
    - `date` string, date, required
    - `summary` string, required
    - `previous_state` string, required
    - `current_state` string, required
    - `tier_direction` 'narrowed' | 'unchanged' | 'widened': Whether the tier we list is worse, the same, or better under current_state than it was under previous_state, judged with both in hand rather than read off change_type. Where it says the tier did not get worse we publish the record and take no risk verdict from it. Absent means we have not judged this record.
    - `impact` 'high' | 'medium' | 'low', required: 'none' whenever standing is 'retracted' — a record we have withdrawn describes no event, so it weighs nothing. Derived at the point of serving; the stored value is left alone.
    - `source_url` string, uri, required
    - `category` string, required
    - `alternatives` string[], required
    - `resolution` ChangeResolution, nullable: Present when the change this record describes is no longer in force. state is 'reversed' when the vendor ended it and 'retracted' when the record was our error. date is the day from which it stopped being in force, or the day we established that, whichever we can source. resolved_by names the change record that ended it, when one exists. A record carrying this field never rates the vendor.
      - `state` 'reversed' | 'retracted', required
      - `date` string, date, required
      - `detail` string
      - `source_url` string, uri
      - `resolved_by` object
        - `vendor` string
        - `date` string, date
        - `change_type` string
    - `standing` 'in_force' | 'reversed' | 'retracted', required: Where the record stands with us. 'in_force' — the change happened and still holds. 'reversed' — it happened and has since been undone, so the record is true history. 'retracted' — the record was our error and we do not stand behind it. Read this rather than testing whether resolution is present: 'reversed' and 'retracted' are different answers to a reader asking whether to trust the record, and the presence of resolution flattens them into one.
    - `date_meaning` 'effective' | 'discovered' | 'effective_by', required: What this record's date means. "effective": the day the change took effect. "discovered": the day we recorded the change; when it took effect is unknown. "effective_by": the latest day the change can have taken effect. Where archived copies of the vendor's page bracket a change, it is dated by the first copy that shows the new terms. Read this field, not date_source: date_source says who wrote the record, not what its date means.
    - `ends_a_free_tier` boolean, required
    - `listing_effect` 'ends' | 'narrows' | 'none'
    - `archive_check` object, nullable: Our check of this record against archived copies of the vendor's page (#1952). Absent where we have not checked the record. Only the outcome no_usable_capture changes how the record counts: it sets no caution or risky label, and where it is the only record that would, the vendor's rating is withheld (rating_withheld, reason unconfirmed) rather than reported as stable; a standing narrowing with it also withholds a favourable stability class; and our pages mark the record unconfirmed. Every other outcome leaves the record counting as it did before the check.
      - `checked` string, date, required: The day we ran the check.
      - `outcome` 'vendor_changed' | 'ours' | 'removal_stated_before' | 'no_usable_capture' | 'text_day_unknown' | 'page_unreadable_today', required: 'vendor_changed' — archived copies show the vendor's page changing. 'ours' — the archived copy from when our listing text was written already states what the record calls new, so the difference was in our text, not on the vendor's page. 'removal_stated_before' — that archived copy already states the removal, so the removal predates our text. 'no_usable_capture' — no archived copy could settle the record either way. 'text_day_unknown' — we do not know when our listing text was written, so there was no capture to compare. 'page_unreadable_today' — we could not read the vendor's page when we ran the check, so no comparison ran.
      - `brackets` object[]: Where outcome is vendor_changed: each pair of archived copies between which the page changed, the last showing the old terms and the first showing the new. Where there is exactly one, date_meaning is effective_by: the change took effect after last_old and on or before date.
        - `last_old` string, date, required: The day of the last copy showing the old terms.
        - `first_new` string, date, nullable, required: The day of the first copy showing the new terms, or null where only our own read of the page shows them.
        - `last_old_capture` string, uri, nullable: That copy's address in the Internet Archive.
        - `first_new_capture` string, uri, nullable: That copy's address in the Internet Archive.
      - `capture_day` string, date, nullable: Where outcome is ours or removal_stated_before: the day of the archived copy from when our listing text was written.
      - `capture` string, uri, nullable: That copy's address in the Internet Archive.
      - `stated_then` string, nullable: The line in that copy that settled the check, as the copy prints it: the terms the record called new (ours), or the removal (removal_stated_before).
      - `recorded_as` 'free_tier_removed' | 'open_source_killed' | 'product_deprecated' | 'limits_reduced' | 'restriction' | 'pricing_restructured' | 'pricing_model_change' | 'limits_increased' | 'new_free_tier' | 'new_tier' | 'startup_program_expanded' | 'pricing_postponed' | 'rebranded' | 'record_corrected', nullable: Where outcome is ours: the change type our re-read recorded. The record's change_type is record_corrected.
  - `total` integer: Records matching your query before paging, counting only the ones served. Retracted records are outside this count unless include_retracted is true, and our own index housekeeping is outside it unless include_index_housekeeping is true. Measured over the same population as all_time_total, so a type-filtered total and the whole-log figure can be compared.
  - `returned` integer
  - `limit` integer
  - `offset` integer
  - `include_retracted` boolean: The value this response was built with, echoed back.
  - `retracted_excluded` integer: How many records matched your query and were withheld for carrying standing 'retracted'. Zero when include_retracted is true, because nothing was withheld. Read this before comparing a count against an earlier one.
  - `include_index_housekeeping` boolean: The value this response was built with, echoed back.
  - `index_housekeeping_excluded` integer: How many records matched your query and were withheld for carrying reports 'our_index'. Zero when include_index_housekeeping is true, because nothing was withheld. Read this before comparing a count against an earlier one.
  - `date_window` ChangeDateWindow: The date filter this response was built under, stated whether or not one ran. A count of 0 means we hold no matching record only when applied is false; when applied is true it means we hold none dated on or after from, and the same query with a wider since may answer differently. The default 30-day window runs on the unfiltered feed alone — a request naming a type, vendor, vendors or category is answered from the whole change log.
    - `applied` boolean, required: Whether any date filter narrowed this response.
    - `from` string, date, nullable, required: The earliest date a record could carry and still be returned. Null when applied is false.
    - `source` 'default' | 'since_parameter' | 'none', required: 'since_parameter' — your since value. 'default' — our window, because the request named no filter. 'none' — no date filter ran.
    - `field` 'date', required: The record field from compares against. date is the effective date only where date_meaning is "effective"; where it is "discovered", date is the day we recorded the change; where it is "effective_by", date is the latest day the change can have taken effect.
    - `note` string, required: The same statement in prose, for a caller reading the response rather than parsing it.
  - `advisory` PublishedDealChange[]: Top 3 high-impact changes outside your filter, drawn from the last 30 days whatever date_window says — this block is what is worth knowing lately, so it stays recent even when your query searches the whole log. Never contains a retracted record, whatever include_retracted says.
    - `vendor` string, required
    - `change_type` 'free_tier_removed' | 'open_source_killed' | 'product_deprecated' | 'limits_reduced' | 'restriction' | 'pricing_restructured' | 'pricing_model_change' | 'limits_increased' | 'new_free_tier' | 'new_tier' | 'startup_program_expanded' | 'pricing_postponed' | 'rebranded' | 'record_corrected', required
    - `reports` 'vendor_offer' | 'our_index': What the record is about. 'vendor_offer' — the vendor changed its own offer, and a vendor page is where that can be read. 'our_index' — we changed what we list while the vendor's offer stood still, so no vendor page evidences it and the record carries no source_url. Absent means vendor_offer. Filter these out of any figure you present as vendor market activity.
    - `date` string, date, required
    - `summary` string, required
    - `previous_state` string, required
    - `current_state` string, required
    - `tier_direction` 'narrowed' | 'unchanged' | 'widened': Whether the tier we list is worse, the same, or better under current_state than it was under previous_state, judged with both in hand rather than read off change_type. Where it says the tier did not get worse we publish the record and take no risk verdict from it. Absent means we have not judged this record.
    - `impact` 'high' | 'medium' | 'low', required: 'none' whenever standing is 'retracted' — a record we have withdrawn describes no event, so it weighs nothing. Derived at the point of serving; the stored value is left alone.
    - `source_url` string, uri, required
    - `category` string, required
    - `alternatives` string[], required
    - `resolution` ChangeResolution, nullable: Present when the change this record describes is no longer in force. state is 'reversed' when the vendor ended it and 'retracted' when the record was our error. date is the day from which it stopped being in force, or the day we established that, whichever we can source. resolved_by names the change record that ended it, when one exists. A record carrying this field never rates the vendor.
      - `state` 'reversed' | 'retracted', required
      - `date` string, date, required
      - `detail` string
      - `source_url` string, uri
      - `resolved_by` object
        - `vendor` string
        - `date` string, date
        - `change_type` string
    - `standing` 'in_force' | 'reversed' | 'retracted', required: Where the record stands with us. 'in_force' — the change happened and still holds. 'reversed' — it happened and has since been undone, so the record is true history. 'retracted' — the record was our error and we do not stand behind it. Read this rather than testing whether resolution is present: 'reversed' and 'retracted' are different answers to a reader asking whether to trust the record, and the presence of resolution flattens them into one.
    - `date_meaning` 'effective' | 'discovered' | 'effective_by', required: What this record's date means. "effective": the day the change took effect. "discovered": the day we recorded the change; when it took effect is unknown. "effective_by": the latest day the change can have taken effect. Where archived copies of the vendor's page bracket a change, it is dated by the first copy that shows the new terms. Read this field, not date_source: date_source says who wrote the record, not what its date means.
    - `ends_a_free_tier` boolean, required
    - `listing_effect` 'ends' | 'narrows' | 'none'
    - `archive_check` object, nullable: Our check of this record against archived copies of the vendor's page (#1952). Absent where we have not checked the record. Only the outcome no_usable_capture changes how the record counts: it sets no caution or risky label, and where it is the only record that would, the vendor's rating is withheld (rating_withheld, reason unconfirmed) rather than reported as stable; a standing narrowing with it also withholds a favourable stability class; and our pages mark the record unconfirmed. Every other outcome leaves the record counting as it did before the check.
      - `checked` string, date, required: The day we ran the check.
      - `outcome` 'vendor_changed' | 'ours' | 'removal_stated_before' | 'no_usable_capture' | 'text_day_unknown' | 'page_unreadable_today', required: 'vendor_changed' — archived copies show the vendor's page changing. 'ours' — the archived copy from when our listing text was written already states what the record calls new, so the difference was in our text, not on the vendor's page. 'removal_stated_before' — that archived copy already states the removal, so the removal predates our text. 'no_usable_capture' — no archived copy could settle the record either way. 'text_day_unknown' — we do not know when our listing text was written, so there was no capture to compare. 'page_unreadable_today' — we could not read the vendor's page when we ran the check, so no comparison ran.
      - `brackets` object[]: Where outcome is vendor_changed: each pair of archived copies between which the page changed, the last showing the old terms and the first showing the new. Where there is exactly one, date_meaning is effective_by: the change took effect after last_old and on or before date.
        - `last_old` string, date, required: The day of the last copy showing the old terms.
        - `first_new` string, date, nullable, required: The day of the first copy showing the new terms, or null where only our own read of the page shows them.
        - `last_old_capture` string, uri, nullable: That copy's address in the Internet Archive.
        - `first_new_capture` string, uri, nullable: That copy's address in the Internet Archive.
      - `capture_day` string, date, nullable: Where outcome is ours or removal_stated_before: the day of the archived copy from when our listing text was written.
      - `capture` string, uri, nullable: That copy's address in the Internet Archive.
      - `stated_then` string, nullable: The line in that copy that settled the check, as the copy prints it: the terms the record called new (ours), or the removal (removal_stated_before).
      - `recorded_as` 'free_tier_removed' | 'open_source_killed' | 'product_deprecated' | 'limits_reduced' | 'restriction' | 'pricing_restructured' | 'pricing_model_change' | 'limits_increased' | 'new_free_tier' | 'new_tier' | 'startup_program_expanded' | 'pricing_postponed' | 'rebranded' | 'record_corrected', nullable: Where outcome is ours: the change type our re-read recorded. The record's change_type is record_corrected.
  - `summary` object
    - `stack_changes_count` integer
    - `ecosystem_high_impact_count` integer
    - `period_days` integer
  - `all_time_total` integer: The whole change log measured by the published rule, unaffected by your query. Equal to change_census.tracked_pricing_changes. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page.
  - `change_census` object: Every total we publish is one of these four and names which. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page. The rule is at /changes#what-counts.
    - `tracked_pricing_changes` integer: tracked pricing changes. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page.
    - `changes_still_in_force` integer: changes still in force. Adds our own bookkeeping: corrections we made to our own records, each dated the day we made it, and index housekeeping — a source we dropped, written up as the vendor deprecating a product.
    - `records_we_stand_behind` integer: records we stand behind. Adds changes the vendor has since reversed. They happened; they no longer hold.
    - `records_held` integer: records in the change log. Adds records we withdrew as our own error. They describe events that did not happen.
    - `retrievable_from_this_door` integer: How many records this request could return before paging. Follows include_retracted and include_index_housekeeping, so it is the only figure here that your query moves.
    - `rule_url` string, uri
    - `note` string
  - `_provenance` Provenance: Where the figures in this response came from and how to attribute them. Every response carrying index data has one, under the key `_provenance`. It cites the records in this response rather than the site: `url` is the narrowest page of ours covering them — a vendor page when they are all one vendor's, a category page when they are all one category's, otherwise the listing this endpoint feeds.
    - `source` string, required: Always AgentDeals.
    - `url` string, uri, required: The page of ours that covers the records in this response.
    - `checked` string, date: The oldest verification date among the records cited here, so the citation is never dated younger than its weakest figure. Absent when no record in the response carries a date.
    - `verified` string, date: The same day as `checked`, absent for the same reason.
    - `cite_as` string, required: A citation line naming the source, the url and the date, ready to reproduce as it stands.
    - `verified_records` integer, required: How many records in this response we rank — the population this citation vouches for.
    - `withheld_records` integer: How many records in this response we return without vouching for: those carrying a `gate`, superseded terms, or a withheld risk level. Absent when there are none.
    - `note` string, required: The request to cite a figure back to where it came from.
    - `gated_note` string: Present whenever `withheld_records` is, stating that the citation does not extend to terms we did not publish.
    - `this_is_a_request_not_an_instruction` string: Present on responses that ask something of the caller, saying the ask is left to the caller and its user rather than imposed on them.

## Other responses

- `400`: Invalid since parameter

---

[API](https://skmtc.dev/agentdeals/apis/agentdeals-api.md) · [All operations](https://skmtc.dev/agentdeals/apis/agentdeals-api/llms.txt) · [OpenAPI document](https://skmtc.dev/agentdeals/apis/agentdeals-api/revisions/2b80deacb326?raw)
